diff --git a/phpunit.xml b/phpunit.xml
index 4cac405..e29cd51 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -12,6 +12,7 @@
stopOnRisky="true"
failOnRisky="true"
failOnEmptyTestSuite="true"
+ displayDetailsOnTestsThatTriggerWarnings="true"
displayDetailsOnIncompleteTests="true"
>
@@ -26,7 +27,8 @@
+
+
-
\ No newline at end of file
diff --git a/src/Abstracts/AbstractResourceRecordType.php b/src/Abstracts/AbstractResourceRecordType.php
index 8bfbcd7..e30a567 100644
--- a/src/Abstracts/AbstractResourceRecordType.php
+++ b/src/Abstracts/AbstractResourceRecordType.php
@@ -125,7 +125,6 @@ protected function parseMessage(): void
'Response header length is invalid'
);
}
-
[
'type' => $type,
'class' => $class,
@@ -201,11 +200,11 @@ public function getHeader(): string
}
/**
- * @return string
+ * @inheritdoc
*/
- public function getMessage(): string
+ public function getMessage(): PacketMessageInterface
{
- return $this->getHeader() . $this->getRData();
+ return $this->message;
}
/**
@@ -315,7 +314,7 @@ public function toArray(): array
'host' => $this->getName(),
'class' => $this->getClass()->getName(),
'ttl' => $this->getTTL(),
- 'type' => $this->getType(),
+ 'type' => $this->getType()->getName(),
'value' => $this->getValue(),
];
}
diff --git a/src/Interfaces/Packet/PacketHeaderInterface.php b/src/Interfaces/Packet/PacketHeaderInterface.php
index 37d33f4..0f931aa 100644
--- a/src/Interfaces/Packet/PacketHeaderInterface.php
+++ b/src/Interfaces/Packet/PacketHeaderInterface.php
@@ -304,6 +304,14 @@ public function withARCount(int $ar) : static;
*/
public function withANCount(int $an) : static;
+ /**
+ * With NS count
+ *
+ * @param int $ns
+ * @return $this
+ */
+ public function withNSCount(int $ns) : static;
+
/**
* With QD Count
* @param int $qd
diff --git a/src/Interfaces/Packet/PacketResourceRecordsInterface.php b/src/Interfaces/Packet/PacketResourceRecordsInterface.php
index 1c1685e..6e9d0e2 100644
--- a/src/Interfaces/Packet/PacketResourceRecordsInterface.php
+++ b/src/Interfaces/Packet/PacketResourceRecordsInterface.php
@@ -46,6 +46,13 @@ public function getRecords(): array;
*/
public function getFilteredType(string $type, bool $single = false) : null|array|ResourceRecordTypeInterface;
+ /**
+ * Return array records
+ * @uses ResourceRecordTypeInterface::toArray()
+ * @return array
+ */
+ public function toArray() : array;
+
/**
* @return Traversable
*/
diff --git a/src/Interfaces/ResourceRecord/ResourceRecordTypeInterface.php b/src/Interfaces/ResourceRecord/ResourceRecordTypeInterface.php
index 092cf40..56cee37 100644
--- a/src/Interfaces/ResourceRecord/ResourceRecordTypeInterface.php
+++ b/src/Interfaces/ResourceRecord/ResourceRecordTypeInterface.php
@@ -74,11 +74,11 @@ public function getOffsetPosition() : int;
public function getHeader() : string;
/**
- * Get message raw response answer data
+ * Get message raw all response answer data
*
- * @return string
+ * @return PacketMessageInterface
*/
- public function getMessage(): string;
+ public function getMessage(): PacketMessageInterface;
/**
* The dns name
@@ -141,7 +141,8 @@ public function getQueryMessage() : string;
/**
* Return array data
*
- * @return array
+ * @return array like dns_get_record()
+ * @see \dns_get_record()
*/
public function toArray() : array;
}
diff --git a/src/Packet/Header.php b/src/Packet/Header.php
index 6654e43..c3acf4c 100644
--- a/src/Packet/Header.php
+++ b/src/Packet/Header.php
@@ -340,6 +340,17 @@ public function withANCount(int $an): static
return $obj;
}
+ /**
+ * @inheritdoc
+ */
+ public function withNSCount(int $ns): static
+ {
+ $obj = clone $this;
+ $obj->nscount = $ns;
+ $obj->message = null;
+ return $obj;
+ }
+
/**
* @inheritdoc
*/
diff --git a/src/Packet/Question.php b/src/Packet/Question.php
index 0b0f8b6..dc46899 100644
--- a/src/Packet/Question.php
+++ b/src/Packet/Question.php
@@ -194,6 +194,7 @@ public function __serialize(): array
* @param int $type
* @param int $class
* @return Question
+ * @noinspection PhpDocMissingThrowsInspection
*/
public static function fromFilteredResponse(
string $name,
diff --git a/src/Packet/Records.php b/src/Packet/Records.php
index 1a40380..8a0916f 100644
--- a/src/Packet/Records.php
+++ b/src/Packet/Records.php
@@ -7,6 +7,8 @@
use ArrayAccess\DnsRecord\Interfaces\ResourceRecord\ResourceRecordTypeInterface;
use ArrayIterator;
use Traversable;
+use function array_map;
+use function array_values;
use function md5;
use function serialize;
use function strtoupper;
@@ -86,6 +88,14 @@ public function getFilteredType(string $type, bool $single = false) : null|array
return $result === [] ? null : $result;
}
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return array_map(static fn ($e) => $e->toArray(), array_values($this->getRecords()));
+ }
+
/**
* @inheritdoc
*/
diff --git a/src/Resolver.php b/src/Resolver.php
index ba45cfb..195192f 100644
--- a/src/Resolver.php
+++ b/src/Resolver.php
@@ -152,7 +152,8 @@ public function createQueryOpcode(
$class = trim($class?:IN::NAME)?:IN::NAME;
$class = Lookup::resourceClass($class);
$type = Lookup::resourceType($type);
- $isOpt = $type->getName() === OPT::TYPE;
+ $typeName = $type->getName();
+ $isOpt = $typeName === OPT::TYPE;
if ($isOpt) { // if is OPT fallback to A
$type = 'A';
}
@@ -165,12 +166,13 @@ public function createQueryOpcode(
}
$dns = new DnsServerStorage(...$ss);
}
+
+ $header = Header::createQueryHeader($opcode, null, $adFlag, $cdFlag, $recurse);
$requestData = new RequestData(
- Header::createQueryHeader($opcode, null, $adFlag, $cdFlag, $recurse),
+ $header,
$dns,
$question
);
-
if ($isOpt || $dnsSec) {
$requestData
->getAdditionalRecords()
diff --git a/src/ResourceRecord/RRTypes/CERT.php b/src/ResourceRecord/RRTypes/CERT.php
index 66befa6..c7cb8bb 100644
--- a/src/ResourceRecord/RRTypes/CERT.php
+++ b/src/ResourceRecord/RRTypes/CERT.php
@@ -5,6 +5,7 @@
use ArrayAccess\DnsRecord\Abstracts\AbstractResourceRecordType;
use function array_values;
+use function base64_encode;
use function substr;
/**
@@ -56,26 +57,27 @@ protected function parseRData(string $message, int $rdataOffset): void
//
// copy the certificate
//
- $this->certificate = substr($this->rData, 5, $this->rdLength - 5);
+ $this->certificate = base64_encode(substr($this->rData, 5, $this->rdLength - 5));
}
- public function getFormat(): ?int
+ public function getFormat(): int
{
- return $this->format??null;
+ return $this->format;
}
- public function getKeyTag(): ?int
+ public function getKeyTag(): int
{
- return $this->keyTag??null;
+ return $this->keyTag;
}
- public function getAlgorithm(): ?string
+ public function getAlgorithm(): string
{
- return $this->algorithm??null;
+ return $this->algorithm;
}
- public function getCertificate(): ?string
+ public function getCertificate(): string
{
- return $this->certificate??null;
+ return $this->certificate;
}
}
+// @todo add toArray()
diff --git a/src/ResourceRecord/RRTypes/CNAME.php b/src/ResourceRecord/RRTypes/CNAME.php
index dbfc4ad..4cce7c3 100644
--- a/src/ResourceRecord/RRTypes/CNAME.php
+++ b/src/ResourceRecord/RRTypes/CNAME.php
@@ -20,18 +20,25 @@ class CNAME extends AbstractResourceRecordType
{
const TYPE = 'CNAME';
- protected string $cname;
-
/**
* @inheritdoc
*/
protected function parseRData(string $message, int $rdataOffset): void
{
- $this->cname = Buffer::readLabel($message, $rdataOffset);
+ $this->value = Buffer::readLabel($message, $rdataOffset);
}
- public function getCname(): string
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
{
- return $this->cname;
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'target' => $this->getValue(),
+ ];
}
}
diff --git a/src/ResourceRecord/RRTypes/DNSKEY.php b/src/ResourceRecord/RRTypes/DNSKEY.php
index 4633e6b..d44683b 100644
--- a/src/ResourceRecord/RRTypes/DNSKEY.php
+++ b/src/ResourceRecord/RRTypes/DNSKEY.php
@@ -113,3 +113,4 @@ protected function parseRData(string $message, int $rdataOffset): void
$this->zoneSep = ((int)$flags[15]) === 1;
}
}
+// @todo add toArray()
diff --git a/src/ResourceRecord/RRTypes/HINFO.php b/src/ResourceRecord/RRTypes/HINFO.php
index 80d649c..0038c7e 100644
--- a/src/ResourceRecord/RRTypes/HINFO.php
+++ b/src/ResourceRecord/RRTypes/HINFO.php
@@ -40,4 +40,29 @@ protected function parseRData(string $message, int $rdataOffset): void
$this->rdLength - strlen($this->cpu)
);
}
+
+ public function getCpu(): string
+ {
+ return $this->cpu;
+ }
+
+ public function getOs(): string
+ {
+ return $this->os;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'cpu' => $this->getCpu(),
+ 'os' => $this->getOs(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/IXFR.php b/src/ResourceRecord/RRTypes/IXFR.php
new file mode 100644
index 0000000..e84e065
--- /dev/null
+++ b/src/ResourceRecord/RRTypes/IXFR.php
@@ -0,0 +1,49 @@
+ |
+ * +---------------------------------------------------+
+ * Authority | JAIN.AD.JP. IN SOA serial=1 |
+ * +---------------------------------------------------+
+ * Additional | |
+ * +---------------------------------------------------+
+ *
+ * IXFR Response Format
+ *
+ * +---------------------------------------------------+
+ * Header | OPCODE=SQUERY, RESPONSE |
+ * +---------------------------------------------------+
+ * Question | QNAME=JAIN.AD.JP., QCLASS=IN, QTYPE=IXFR |
+ * +---------------------------------------------------+
+ * Answer | JAIN.AD.JP. IN SOA serial=3 |
+ * | JAIN.AD.JP. IN NS NS.JAIN.AD.JP. |
+ * | NS.JAIN.AD.JP. IN A 133.69.136.1 |
+ * | JAIN-BB.JAIN.AD.JP. IN A 133.69.136.3 |
+ * | JAIN-BB.JAIN.AD.JP. IN A 192.41.197.2 |
+ * | JAIN.AD.JP. IN SOA serial=3 |
+ * +---------------------------------------------------+
+ * Authority | |
+ * +---------------------------------------------------+
+ * Additional | |
+ * +---------------------------------------------------+
+ *
+ * @link https://datatracker.ietf.org/doc/html/rfc1995#section-7
+ */
+class IXFR extends AbstractResourceRecordType implements ResourceRecordMetaTypeInterface
+{
+ const TYPE = 'IXFR';
+}
+// @todo add completion()
diff --git a/src/ResourceRecord/RRTypes/MG.php b/src/ResourceRecord/RRTypes/MG.php
index 07bb76e..1967893 100644
--- a/src/ResourceRecord/RRTypes/MG.php
+++ b/src/ResourceRecord/RRTypes/MG.php
@@ -31,3 +31,4 @@ protected function parseRData(string $message, int $rdataOffset): void
$this->value = Buffer::readLabel($message, $rdataOffset);
}
}
+// @todo add toArray()
diff --git a/src/ResourceRecord/RRTypes/MR.php b/src/ResourceRecord/RRTypes/MR.php
index e5d361f..ac22d21 100644
--- a/src/ResourceRecord/RRTypes/MR.php
+++ b/src/ResourceRecord/RRTypes/MR.php
@@ -31,3 +31,4 @@ protected function parseRData(string $message, int $rdataOffset): void
$this->value = Buffer::readLabel($message, $rdataOffset);
}
}
+// @todo add toArray()
diff --git a/src/ResourceRecord/RRTypes/MX.php b/src/ResourceRecord/RRTypes/MX.php
index e4d84ac..14bdc10 100644
--- a/src/ResourceRecord/RRTypes/MX.php
+++ b/src/ResourceRecord/RRTypes/MX.php
@@ -55,4 +55,19 @@ public function getExchange(): string
{
return $this->exchange;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'pri' => $this->getPreference(),
+ 'target' => $this->getExchange(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/NS.php b/src/ResourceRecord/RRTypes/NS.php
index ca03dbf..c3c9493 100644
--- a/src/ResourceRecord/RRTypes/NS.php
+++ b/src/ResourceRecord/RRTypes/NS.php
@@ -30,4 +30,18 @@ protected function parseRData(string $message, int $rdataOffset): void
{
$this->value = Buffer::readLabel($message, $rdataOffset);
}
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'target' => $this->getValue(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/OPT.php b/src/ResourceRecord/RRTypes/OPT.php
index 50bdfc8..f3aa9e6 100644
--- a/src/ResourceRecord/RRTypes/OPT.php
+++ b/src/ResourceRecord/RRTypes/OPT.php
@@ -115,7 +115,7 @@ public function getQueryMessage(): string
// build the TTL value based on the local values
//
return pack(
- 'CCCC',
+ 'C4',
$this->extended_rcode,
$this->version,
($this->do << 7),
@@ -123,3 +123,4 @@ public function getQueryMessage(): string
);
}
}
+// @todo add toArray()
diff --git a/src/ResourceRecord/RRTypes/PTR.php b/src/ResourceRecord/RRTypes/PTR.php
index 3768a10..34b821c 100644
--- a/src/ResourceRecord/RRTypes/PTR.php
+++ b/src/ResourceRecord/RRTypes/PTR.php
@@ -29,4 +29,18 @@ protected function parseRData(string $message, int $rdataOffset): void
// read domain name space
$this->value = Buffer::readLabel($message, $rdataOffset);
}
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'target' => $this->getValue(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/RRSIG.php b/src/ResourceRecord/RRTypes/RRSIG.php
index 22bedc2..389f166 100644
--- a/src/ResourceRecord/RRTypes/RRSIG.php
+++ b/src/ResourceRecord/RRTypes/RRSIG.php
@@ -14,12 +14,19 @@ class RRSIG extends AbstractResourceRecordType
const TYPE = 'RRSIG';
protected int $sigType;
+
protected int $algorithm;
+
protected int $labels;
+
protected int $originalttl;
+
protected int $expiration;
+
protected int $inception;
+
protected int $keyTag;
+
protected string $signer;
protected string $signature;
@@ -30,7 +37,6 @@ class RRSIG extends AbstractResourceRecordType
protected function parseRData(string $message, int $rdataOffset): void
{
$stuff = Buffer::read($message, $rdataOffset, 18);
- //$length = $ans_header['length'] - 18;
[
'type' => $this->sigType,
'algorithm' => $this->algorithm,
@@ -49,11 +55,6 @@ protected function parseRData(string $message, int $rdataOffset): void
);
}
- public function getType(): string
- {
- return $this->type;
- }
-
public function getSigType(): int
{
return $this->sigType;
@@ -98,4 +99,22 @@ public function getSignature(): string
{
return $this->signature;
}
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'ttl' => $this->getTTL(),
+ 'class' => $this->getClass()->getName(),
+ 'type' => $this->getType()->getName(),
+ 'labels' => $this->getLabels(),
+ 'sigtype' => $this->getSigType(),
+ 'originalttl' => $this->getOriginalttl(),
+ 'expiration' => $this->getExpiration(),
+ 'inception' => $this->getInception(),
+ 'keytag' => $this->getKeyTag(),
+ 'algorithm' => $this->getAlgorithm(),
+ 'signer' => $this->getSigner(),
+ 'signature' => $this->getSignature(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/SOA.php b/src/ResourceRecord/RRTypes/SOA.php
index a31da96..f1a9bcc 100644
--- a/src/ResourceRecord/RRTypes/SOA.php
+++ b/src/ResourceRecord/RRTypes/SOA.php
@@ -43,6 +43,7 @@ protected function parseRData($message, int $rdataOffset): void
"Nserial/Nrefresh/Nretry/Nexpire/NminTTL",
Buffer::read($message, $rdataOffset, 20)
);
+
$this->value = sprintf(
'%s. %s. %d %d %d %d %d',
$this->mName,
@@ -55,25 +56,6 @@ protected function parseRData($message, int $rdataOffset): void
);
}
- /**
- * @return string
- * @link https://datatracker.ietf.org/doc/rfc1995/
- */
-// public function getQueryMessage(): string
-// {
-// $query = Lookup::compressLabel($this->mName);
-// $query .= Lookup::compressLabel($this->rName);
-// $query .= pack('N*', $this->serial, $this->refresh, $this->retry, $this->expire, $this->minimumTTL);
-// return $query;
-// return Lookup::compressLabel(
-// sprintf(
-// '%s. IN SOA serial=%d',
-// $this->name,
-// $this->serial
-// )
-// );
-// }
-
public function getMinimumTTL(): int
{
return $this->minimumTTL;
@@ -108,4 +90,24 @@ public function getRetry(): int
{
return $this->retry;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'mname' => $this->getMName(),
+ 'rname' => $this->getRName(),
+ 'serial' => $this->getSerial(),
+ 'refresh' => $this->getRefresh(),
+ 'retry' => $this->getRetry(),
+ 'expire' => $this->getExpire(),
+ 'minimum-ttl' => $this->getMinimumTTL(),
+ ];
+ }
}
diff --git a/src/ResourceRecord/RRTypes/SRV.php b/src/ResourceRecord/RRTypes/SRV.php
index be46371..36dfe87 100644
--- a/src/ResourceRecord/RRTypes/SRV.php
+++ b/src/ResourceRecord/RRTypes/SRV.php
@@ -21,8 +21,6 @@ class SRV extends AbstractResourceRecordType
protected int $port;
- protected string $target;
-
/**
* @inheritdoc
*/
@@ -38,6 +36,35 @@ protected function parseRData($message, int $rdataOffset): void
Buffer::read($this->rData, $offset, 6)
);
- $this->target = Buffer::readLabel($this->rData, $offset);
+ $this->value = Buffer::readLabel($this->rData, $offset);
+ }
+
+ public function getPriority(): int
+ {
+ return $this->priority;
+ }
+
+ public function getWeight(): int
+ {
+ return $this->weight;
+ }
+
+ public function getPort(): int
+ {
+ return $this->port;
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'host' => $this->getName(),
+ 'class' => $this->getClass()->getName(),
+ 'ttl' => $this->getTTL(),
+ 'type' => $this->getType()->getName(),
+ 'pri' => $this->getPriority(),
+ 'weight' => $this->getWeight(),
+ 'port' => $this->getPort(),
+ 'target' => $this->getValue(),
+ ];
}
}
diff --git a/src/Utils/Buffer.php b/src/Utils/Buffer.php
index 2375c4f..01d3830 100644
--- a/src/Utils/Buffer.php
+++ b/src/Utils/Buffer.php
@@ -53,21 +53,33 @@ public static function read(string $buffer, int &$offset, int $length): string
public static function readLabel(string $buffer, int &$offset, string $delimiter = '.'): string
{
+ $bufferLength = strlen($buffer);
+ if ($bufferLength <= $offset) {
+ return '';
+ }
$out = [];
- while (($length = ord(self::read($buffer, $offset, 1))) > 0) {
- if ($length < 64) {
- $out[] = self::read($buffer, $offset, $length);
- continue;
+ while (true) {
+ if ($bufferLength <= $offset) {
+ break;
}
- // 0x3f = 63
- $currentPosition = (($length & 63) << 8) + ord(self::read($buffer, $offset, 1));
- while (($len = ord(substr($buffer, $currentPosition, 1))) && $len > 0) {
- $out[] = substr($buffer, $currentPosition + 1, $len);
- $currentPosition += $len + 1;
+ $length = ord($buffer[$offset]);
+ if ($length === 0) {
+ ++$offset;
+ break;
+ } elseif (($length & 0xc0) === 0xc0) {
+ $pointer = ord($buffer[$offset]) << 8 | ord($buffer[$offset+1]);
+ $pointer = $pointer & 0x3fff;
+ $name2 = self::readLabel($buffer, $pointer);
+ $out[] = $name2;
+ $offset += 2;
+ break;
+ } else {
+ ++$offset;
+ $elem = substr($buffer, $offset, $length);
+ $out[] = $elem;
+ $offset += $length;
}
- break;
}
-
return implode($delimiter, $out);
}