View file File name : Message.php Content :<?php declare(strict_types=1); /* * 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; use Badcow\DNS\Rdata\DecodeException; use Badcow\DNS\Rdata\UnsupportedTypeException; class Message { /** * ID. * * @var int */ private $id; /** * QR. * * @var bool */ private $isResponse; /** * OPCODE. * * @var int */ private $opcode; /** * AA. * * @var bool */ private $isAuthoritative; /** * TC. * * @var bool */ private $isTruncated; /** * RD. * * @var bool */ private $isRecursionDesired; /** * RA. * * @var bool */ private $isRecursionAvailable; /** * Bit 9 of the header flags. * * @var int */ private $bit9 = 0; /** * AD. * * {@link https://tools.ietf.org/html/rfc4035#section-3.2.3} * * @var bool */ private $isAuthenticData; /** * CD. * * {@link https://tools.ietf.org/html/rfc4035#section-3.2.2} * * @var bool */ private $isCheckingDisabled; /** * RCODE. * * @var int */ private $rcode; /** * @var Question[] */ private $questions = []; /** * @var ResourceRecord[] */ private $answers = []; /** * @var ResourceRecord[] */ private $authoritatives = []; /** * @var ResourceRecord[] */ private $additionals = []; /** * Encode a domain name as a sequence of labels. */ public static function encodeName(string $name): string { if ('.' === $name) { return chr(0); } $name = rtrim($name, '.').'.'; $res = ''; foreach (explode('.', $name) as $label) { $res .= chr(strlen($label)).$label; } return $res; } public static function decodeName(string $string, int &$offset = 0): string { $len = ord($string[$offset]); ++$offset; $isCompressed = (bool) (0b11000000 & $len); $_offset = 0; if ($isCompressed) { $_offset = $offset + 1; $offset = (0b00111111 & $len) * 256 + ord($string[$offset]); $len = ord($string[$offset]); ++$offset; } if (0 === $len) { return '.'; } $name = ''; while (0 !== $len) { $name .= substr($string, $offset, $len).'.'; $offset += $len; $len = ord($string[$offset]); if ($len & 0b11000000) { $name .= self::decodeName($string, $offset); break; } ++$offset; } if ($isCompressed) { $offset = $_offset; } return $name; } public function getId(): int { return $this->id; } public function setId(int $id): void { $this->id = $id; } public function isResponse(): bool { return $this->isResponse; } public function setResponse(bool $isResponse): void { $this->isResponse = $isResponse; } public function isQuery(): bool { return !$this->isResponse; } public function setQuery(bool $query): void { $this->setResponse(!$query); } public function getOpcode(): int { return $this->opcode; } public function setOpcode(int $opcode): void { $this->opcode = $opcode; } public function isAuthoritative(): bool { return $this->isAuthoritative; } public function setAuthoritative(bool $isAuthoritative): void { $this->isAuthoritative = $isAuthoritative; } public function isTruncated(): bool { return $this->isTruncated; } public function setTruncated(bool $isTruncated): void { $this->isTruncated = $isTruncated; } public function isRecursionDesired(): bool { return $this->isRecursionDesired; } public function setRecursionDesired(bool $isRecursionDesired): void { $this->isRecursionDesired = $isRecursionDesired; } public function isRecursionAvailable(): bool { return $this->isRecursionAvailable; } public function setRecursionAvailable(bool $isRecursionAvailable): void { $this->isRecursionAvailable = $isRecursionAvailable; } public function getBit9(): int { return $this->bit9; } public function setBit9(int $bit9): void { $this->bit9 = $bit9; } public function isAuthenticData(): bool { return $this->isAuthenticData; } public function setAuthenticData(bool $isAuthenticData): void { $this->isAuthenticData = $isAuthenticData; } public function isCheckingDisabled(): bool { return $this->isCheckingDisabled; } public function setCheckingDisabled(bool $isCheckingDisabled): void { $this->isCheckingDisabled = $isCheckingDisabled; } public function getRcode(): int { return $this->rcode; } public function setRcode(int $rcode): void { $this->rcode = $rcode; } /** * @return Question[] */ public function getQuestions(): array { return $this->questions; } public function addQuestion(Question $question): void { $this->questions[] = $question; } /** * @param Question[] $questions */ public function setQuestions(array $questions): void { $this->questions = []; foreach ($questions as $question) { $this->addQuestion($question); } } /** * @return ResourceRecord[] */ public function getAnswers(): array { return $this->answers; } public function addAnswer(ResourceRecord $answer): void { $this->answers[] = $answer; } /** * @param ResourceRecord[] $answers */ public function setAnswers(array $answers): void { $this->answers = []; foreach ($answers as $answer) { $this->addAnswer($answer); } } /** * @return ResourceRecord[] */ public function getAuthoritatives(): array { return $this->authoritatives; } public function addAuthoritative(ResourceRecord $authoritative): void { $this->authoritatives[] = $authoritative; } /** * @param ResourceRecord[] $authoritatives */ public function setAuthoritatives(array $authoritatives): void { $this->authoritatives = []; foreach ($authoritatives as $authoritative) { $this->addAuthoritative($authoritative); } } /** * @return ResourceRecord[] */ public function getAdditionals(): array { return $this->additionals; } public function addAdditional(ResourceRecord $additional): void { $this->additionals[] = $additional; } /** * @param ResourceRecord[] $additionals */ public function setAdditionals(array $additionals): void { $this->additionals = []; foreach ($additionals as $additional) { $this->addAdditional($additional); } } public function countQuestions(): int { return count($this->questions); } public function countAnswers(): int { return count($this->answers); } public function countAuthoritatives(): int { return count($this->authoritatives); } public function countAdditionals(): int { return count($this->additionals); } /** * @throws UnsetValueException */ public function toWire(): string { $flags = 0x0 | ($this->isResponse & 0x1) << 15 | ($this->opcode & 0xF) << 11 | ($this->isAuthoritative & 0x1) << 10 | ($this->isTruncated & 0x1) << 9 | ($this->isRecursionDesired & 0x1) << 8 | ($this->isRecursionAvailable & 0x1) << 7 | ($this->bit9 & 0x1) << 6 | ($this->isAuthenticData & 0x1) << 5 | ($this->isCheckingDisabled & 0x1) << 4 | ($this->rcode & 0xF); $encoded = pack( 'nnnnnn', $this->id, $flags, $this->countQuestions(), $this->countAnswers(), $this->countAuthoritatives(), $this->countAdditionals() ); foreach (array_merge($this->questions, $this->answers, $this->authoritatives, $this->additionals) as $resource) { /* @var ResourceRecord|Question $resource */ $encoded .= $resource->toWire(); } return $encoded; } /** * @throws UnsupportedTypeException */ public static function fromWire(string $encoded): Message { $message = new self(); $offset = 0; if (false === $header = unpack('nid/nflags/nqdcount/nancount/nnscount/narcount', $encoded, $offset)) { throw new \UnexpectedValueException(sprintf('Malformed header encountered. "%s"', DecodeException::binaryToHex($encoded))); } $offset += 12; $flags = $header['flags']; $message->setId($header['id']); $message->setResponse((bool) ($flags >> 15 & 0x1)); $message->setOpcode($flags >> 11 & 0xF); $message->setAuthoritative((bool) ($flags >> 10 & 0x1)); $message->setTruncated((bool) ($flags >> 9 & 0x1)); $message->setRecursionDesired((bool) ($flags >> 8 & 0x1)); $message->setRecursionAvailable((bool) ($flags >> 7 & 0x1)); $message->setBit9($flags >> 6 & 0x1); $message->setAuthenticData((bool) ($flags >> 5 & 0x1)); $message->setCheckingDisabled((bool) ($flags >> 4 & 0x1)); $message->setRcode($flags & 0xF); for ($i = 0; $i < $header['qdcount']; ++$i) { $message->addQuestion(Question::fromWire($encoded, $offset)); } $rrs = []; while ($offset < strlen($encoded)) { $rrs[] = ResourceRecord::fromWire($encoded, $offset); } $message->setAnswers(array_splice($rrs, 0, $header['ancount'])); $message->setAuthoritatives(array_splice($rrs, 0, $header['nscount'])); $message->setAdditionals(array_splice($rrs, 0, $header['arcount'])); return $message; } }