Skip to content

Commit

Permalink
Implement apex
Browse files Browse the repository at this point in the history
  • Loading branch information
ben221199 committed Feb 23, 2025
1 parent 0449cb6 commit 13a7c2f
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 4 deletions.
69 changes: 67 additions & 2 deletions src/Fields/FQDN.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ public function getValue(): array{
* @return bool
*/
public function isAbsolute(): bool{
return $this->value[count($this->value)-1]==='';
return ($this->value[count($this->value)-1] ?? null)==='';
}

/**
* @return bool
*/
public function isApex(): bool{
return count($this->value)===0;
}

/**
Expand All @@ -46,12 +53,61 @@ public function isRelative(): bool{
return !$this->isAbsolute();
}

/**
* @param FQDN $origin
* @param ?bool|null $ignoreCurrentState
* @return FQDN
* @throws DNSFieldException
*/
public function makeAbsolute(FQDN $origin,?bool $ignoreCurrentState=false): FQDN{
if($origin->isRelative()){
throw new DNSFieldException('Origin FQDN cannot be relative.');
}
if($this->isAbsolute()){
if($ignoreCurrentState){
return $this;
}
throw new DNSFieldException("FQDN already absolute.");
}
return new FQDN(...array_merge($this->value,$origin->value));
}

/**
* @param FQDN $origin
* @param ?bool|null $ignoreCurrentState
* @return FQDN
* @throws DNSFieldException
*/
public function makeRelative(FQDN $origin,?bool $ignoreCurrentState=false): FQDN{
if($origin->isRelative()){
throw new DNSFieldException('Origin FQDN cannot be relative.');
}
if($this->isRelative()){
if($ignoreCurrentState){
return $this;
}
throw new DNSFieldException("FQDN already relative.");
}
for($i=0;$i<count($origin->value);$i++){
if(($origin->value[count($origin->value)-$i] ?? null)!==($this->value[count($this->value)-$i] ?? null)){
if($ignoreCurrentState){
return $this;
}
throw new DNSFieldException("FQDN is not subordinate to origin.");
}
}
return new FQDN(...array_slice($this->value,0,count($this->value)-count($origin->value)));
}

/**
* @return string
*/
public function serializeToPresentationFormat(): string{
if($this->isApex()){
return '@';
}
return implode('.',array_map(static function($label){
return str_replace('.','\.',$label);
return str_replace(['@','.'],['\@','\.'],$label);
},$this->value));
}

Expand All @@ -76,6 +132,15 @@ public function serializeToWireFormat(): string{
*/
public static function deserializeFromPresentationFormat(string $data): FQDN{
$labels = preg_split('/(?<!\\\\)\\./',$data);
if(count($labels)===1 && ($labels[0] ?? null)==='@'){
return new FQDN();
}
foreach($labels AS &$label){
if($label==='@'){
throw new DNSFieldException('At-sign cannot appear without backslash when having multiple labels.');
}
$label = str_replace(['\@','\.'],['@','.'],$label);
}
return new self(...$labels);
}

Expand Down
92 changes: 92 additions & 0 deletions tests/Fields/FQDNTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ class FQDNTest extends TestCase{
* @throws DNSFieldException
*/
public function testConstructor(): void{
self::assertInstanceOf(FQDN::class,new FQDN());
self::assertInstanceOf(FQDN::class,new FQDN('@'));
self::assertInstanceOf(FQDN::class,new FQDN('dotted.label'));
self::assertInstanceOf(FQDN::class,new FQDN('@','dotted.label'));

self::assertInstanceOf(FQDN::class,new FQDN('example','com'));
self::assertInstanceOf(FQDN::class,new FQDN('example','com',''));
self::assertInstanceOf(FQDN::class,new FQDN('www','example','com'));
Expand Down Expand Up @@ -46,6 +51,11 @@ public function testTooLongDomainName(): void{
* @throws DNSFieldException
*/
public function testGetValue(): void{
self::assertSame([],(new FQDN())->getValue());
self::assertSame(['@'],(new FQDN('@'))->getValue());
self::assertSame(['dotted.label'],(new FQDN('dotted.label'))->getValue());
self::assertSame(['@','dotted.label'],(new FQDN('@','dotted.label'))->getValue());

self::assertSame(['example','com'],(new FQDN('example','com'))->getValue());
self::assertSame(['example','com',''],(new FQDN('example','com',''))->getValue());
self::assertSame(['www','example','com'],(new FQDN('www','example','com'))->getValue());
Expand All @@ -57,28 +67,84 @@ public function testGetValue(): void{
* @throws DNSFieldException
*/
public function testIsAbsolute(){
self::assertFalse((new FQDN())->isAbsolute());
self::assertFalse((new FQDN('@'))->isAbsolute());
self::assertFalse((new FQDN('dotted.label'))->isAbsolute());
self::assertFalse((new FQDN('@','dotted.label'))->isAbsolute());

self::assertFalse((new FQDN('example','com'))->isAbsolute());
self::assertTrue((new FQDN('example','com',''))->isAbsolute());
self::assertFalse((new FQDN('www','example','com'))->isAbsolute());
self::assertTrue((new FQDN('www','example','com',''))->isAbsolute());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testIsApex(){
self::assertTrue((new FQDN())->isApex());
self::assertFalse((new FQDN('@'))->isApex());

self::assertFalse((new FQDN('example','com'))->isApex());
self::assertFalse((new FQDN('@','example','com'))->isApex());

self::assertFalse((new FQDN('example','com',''))->isApex());
self::assertFalse((new FQDN('@','example','com',''))->isApex());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testIsRelative(){
self::assertTrue((new FQDN())->isRelative());
self::assertTrue((new FQDN('@'))->isRelative());
self::assertTrue((new FQDN('dotted.label'))->isRelative());
self::assertTrue((new FQDN('@','dotted.label'))->isRelative());

self::assertTrue((new FQDN('example','com'))->isRelative());
self::assertFalse((new FQDN('example','com',''))->isRelative());
self::assertTrue((new FQDN('www','example','com'))->isRelative());
self::assertFalse((new FQDN('www','example','com',''))->isRelative());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testMakeAbsolute(){
$origin = new FQDN('example','com','');

self::assertSame(['example','com','example','com',''],(new FQDN('example','com'))->makeAbsolute($origin,true)->getValue());
self::assertSame(['example','com',''],(new FQDN('example','com',''))->makeAbsolute($origin,true)->getValue());
self::assertSame(['www','example','com','example','com',''],(new FQDN('www','example','com'))->makeAbsolute($origin,true)->getValue());
self::assertSame(['www','example','com',''],(new FQDN('www','example','com',''))->makeAbsolute($origin,true)->getValue());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testMakeRelative(){
$origin = new FQDN('example','com','');

self::assertSame(['example','com'],(new FQDN('example','com','example','com',''))->makeRelative($origin,true)->getValue());
self::assertSame([],(new FQDN('example','com',''))->makeRelative($origin,true)->getValue());
self::assertSame(['www','example','com'],(new FQDN('www','example','com','example','com',''))->makeRelative($origin,true)->getValue());
self::assertSame(['www'],(new FQDN('www','example','com',''))->makeRelative($origin,true)->getValue());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testSerializeToPresentationFormat(): void{
self::assertSame('@',(new FQDN())->serializeToPresentationFormat());
self::assertSame('\@',(new FQDN('@'))->serializeToPresentationFormat());
self::assertSame('dotted\.label',(new FQDN('dotted.label'))->serializeToPresentationFormat());
self::assertSame('\@.dotted\.label',(new FQDN('@','dotted.label'))->serializeToPresentationFormat());

self::assertSame('example.com',(new FQDN('example','com'))->serializeToPresentationFormat());
self::assertSame('example.com.',(new FQDN('example','com',''))->serializeToPresentationFormat());
self::assertSame('www.example.com',(new FQDN('www','example','com'))->serializeToPresentationFormat());
Expand All @@ -90,6 +156,11 @@ public function testSerializeToPresentationFormat(): void{
* @throws DNSFieldException
*/
public function testSerializeToWireFormat(): void{
self::assertSame("\x40",(new FQDN())->serializeToWireFormat());
self::assertSame("\x01@\x40",(new FQDN('@'))->serializeToWireFormat());
self::assertSame("\x0Cdotted.label\x40",(new FQDN('dotted.label'))->serializeToWireFormat());
self::assertSame("\x01@\x0Cdotted.label\x40",(new FQDN('@','dotted.label'))->serializeToWireFormat());

self::assertSame("\x07example\x03com\x40",(new FQDN('example','com'))->serializeToWireFormat());
self::assertSame("\x07example\x03com\x00",(new FQDN('example','com',''))->serializeToWireFormat());
self::assertSame("\x03www\x07example\x03com\x40",(new FQDN('www','example','com'))->serializeToWireFormat());
Expand All @@ -101,17 +172,38 @@ public function testSerializeToWireFormat(): void{
* @throws DNSFieldException
*/
public function testDeserializeFromPresentationFormat(): void{
self::assertSame([],FQDN::deserializeFromPresentationFormat('@')->getValue());
self::assertSame(['@'],FQDN::deserializeFromPresentationFormat('\@')->getValue());
self::assertSame(['dotted.label'],FQDN::deserializeFromPresentationFormat('dotted\.label')->getValue());
self::assertSame(['@','dotted.label'],FQDN::deserializeFromPresentationFormat('\@.dotted\.label')->getValue());

self::assertSame(['example','com'],FQDN::deserializeFromPresentationFormat('example.com')->getValue());
self::assertSame(['example','com',''],FQDN::deserializeFromPresentationFormat('example.com.')->getValue());
self::assertSame(['www','example','com'],FQDN::deserializeFromPresentationFormat('www.example.com')->getValue());
self::assertSame(['www','example','com',''],FQDN::deserializeFromPresentationFormat('www.example.com.')->getValue());
}

/**
* @return void
* @throws DNSFieldException
*/
public function testDeserializeFromPresentationFormatAtSign(): void{
self::expectException(DNSFieldException::class);
self::expectExceptionMessage('At-sign cannot appear without backslash when having multiple labels.');

FQDN::deserializeFromPresentationFormat('@.dotted\.label');
}

/**
* @return void
* @throws DNSFieldException
*/
public function testDeserializeFromWireFormat(): void{
self::assertSame([],FQDN::deserializeFromWireFormat("\x40")->getValue());
self::assertSame(['@'],FQDN::deserializeFromWireFormat("\x01@\x40")->getValue());
self::assertSame(['dotted.label'],FQDN::deserializeFromWireFormat("\x0Cdotted.label\x40")->getValue());
self::assertSame(['@','dotted.label'],FQDN::deserializeFromWireFormat("\x01@\x0Cdotted.label\x40")->getValue());

self::assertSame(['example','com'],FQDN::deserializeFromWireFormat("\x07example\x03com\x40")->getValue());
self::assertSame(['example','com',''],FQDN::deserializeFromWireFormat("\x07example\x03com\x00")->getValue());
self::assertSame(['www','example','com'],FQDN::deserializeFromWireFormat("\x03www\x07example\x03com\x40")->getValue());
Expand Down
4 changes: 2 additions & 2 deletions tests/TypesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ public function testTypes(){
self::assertSame("\x02ns\x07example\x03com\x00",$nsRecord->serializeToWireFormat());

$mdRecord = new MD([
new FQDN('@'),
new FQDN(),
]);
self::assertSame('@',$mdRecord->serializeToPresentationFormat());
self::assertSame("\x01@\x40",$mdRecord->serializeToWireFormat());
self::assertSame("\x40",$mdRecord->serializeToWireFormat());

$soaRecord = new SOA([
new FQDN('ns','example','com',''),
Expand Down

0 comments on commit 13a7c2f

Please sign in to comment.