summaryrefslogtreecommitdiffstats
path: root/vendor/react/dns/src/Protocol/BinaryDumper.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/react/dns/src/Protocol/BinaryDumper.php')
-rw-r--r--vendor/react/dns/src/Protocol/BinaryDumper.php199
1 files changed, 199 insertions, 0 deletions
diff --git a/vendor/react/dns/src/Protocol/BinaryDumper.php b/vendor/react/dns/src/Protocol/BinaryDumper.php
new file mode 100644
index 0000000..6f4030f
--- /dev/null
+++ b/vendor/react/dns/src/Protocol/BinaryDumper.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace React\Dns\Protocol;
+
+use React\Dns\Model\Message;
+use React\Dns\Model\Record;
+use React\Dns\Query\Query;
+
+final class BinaryDumper
+{
+ /**
+ * @param Message $message
+ * @return string
+ */
+ public function toBinary(Message $message)
+ {
+ $data = '';
+
+ $data .= $this->headerToBinary($message);
+ $data .= $this->questionToBinary($message->questions);
+ $data .= $this->recordsToBinary($message->answers);
+ $data .= $this->recordsToBinary($message->authority);
+ $data .= $this->recordsToBinary($message->additional);
+
+ return $data;
+ }
+
+ /**
+ * @param Message $message
+ * @return string
+ */
+ private function headerToBinary(Message $message)
+ {
+ $data = '';
+
+ $data .= pack('n', $message->id);
+
+ $flags = 0x00;
+ $flags = ($flags << 1) | ($message->qr ? 1 : 0);
+ $flags = ($flags << 4) | $message->opcode;
+ $flags = ($flags << 1) | ($message->aa ? 1 : 0);
+ $flags = ($flags << 1) | ($message->tc ? 1 : 0);
+ $flags = ($flags << 1) | ($message->rd ? 1 : 0);
+ $flags = ($flags << 1) | ($message->ra ? 1 : 0);
+ $flags = ($flags << 3) | 0; // skip unused zero bit
+ $flags = ($flags << 4) | $message->rcode;
+
+ $data .= pack('n', $flags);
+
+ $data .= pack('n', count($message->questions));
+ $data .= pack('n', count($message->answers));
+ $data .= pack('n', count($message->authority));
+ $data .= pack('n', count($message->additional));
+
+ return $data;
+ }
+
+ /**
+ * @param Query[] $questions
+ * @return string
+ */
+ private function questionToBinary(array $questions)
+ {
+ $data = '';
+
+ foreach ($questions as $question) {
+ $data .= $this->domainNameToBinary($question->name);
+ $data .= pack('n*', $question->type, $question->class);
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param Record[] $records
+ * @return string
+ */
+ private function recordsToBinary(array $records)
+ {
+ $data = '';
+
+ foreach ($records as $record) {
+ /* @var $record Record */
+ switch ($record->type) {
+ case Message::TYPE_A:
+ case Message::TYPE_AAAA:
+ $binary = \inet_pton($record->data);
+ break;
+ case Message::TYPE_CNAME:
+ case Message::TYPE_NS:
+ case Message::TYPE_PTR:
+ $binary = $this->domainNameToBinary($record->data);
+ break;
+ case Message::TYPE_TXT:
+ case Message::TYPE_SPF:
+ $binary = $this->textsToBinary($record->data);
+ break;
+ case Message::TYPE_MX:
+ $binary = \pack(
+ 'n',
+ $record->data['priority']
+ );
+ $binary .= $this->domainNameToBinary($record->data['target']);
+ break;
+ case Message::TYPE_SRV:
+ $binary = \pack(
+ 'n*',
+ $record->data['priority'],
+ $record->data['weight'],
+ $record->data['port']
+ );
+ $binary .= $this->domainNameToBinary($record->data['target']);
+ break;
+ case Message::TYPE_SOA:
+ $binary = $this->domainNameToBinary($record->data['mname']);
+ $binary .= $this->domainNameToBinary($record->data['rname']);
+ $binary .= \pack(
+ 'N*',
+ $record->data['serial'],
+ $record->data['refresh'],
+ $record->data['retry'],
+ $record->data['expire'],
+ $record->data['minimum']
+ );
+ break;
+ case Message::TYPE_CAA:
+ $binary = \pack(
+ 'C*',
+ $record->data['flag'],
+ \strlen($record->data['tag'])
+ );
+ $binary .= $record->data['tag'];
+ $binary .= $record->data['value'];
+ break;
+ case Message::TYPE_SSHFP:
+ $binary = \pack(
+ 'CCH*',
+ $record->data['algorithm'],
+ $record->data['type'],
+ $record->data['fingerprint']
+ );
+ break;
+ case Message::TYPE_OPT:
+ $binary = '';
+ foreach ($record->data as $opt => $value) {
+ if ($opt === Message::OPT_TCP_KEEPALIVE && $value !== null) {
+ $value = \pack('n', round($value * 10));
+ }
+ $binary .= \pack('n*', $opt, \strlen((string) $value)) . $value;
+ }
+ break;
+ default:
+ // RDATA is already stored as binary value for unknown record types
+ $binary = $record->data;
+ }
+
+ $data .= $this->domainNameToBinary($record->name);
+ $data .= \pack('nnNn', $record->type, $record->class, $record->ttl, \strlen($binary));
+ $data .= $binary;
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param string[] $texts
+ * @return string
+ */
+ private function textsToBinary(array $texts)
+ {
+ $data = '';
+ foreach ($texts as $text) {
+ $data .= \chr(\strlen($text)) . $text;
+ }
+ return $data;
+ }
+
+ /**
+ * @param string $host
+ * @return string
+ */
+ private function domainNameToBinary($host)
+ {
+ if ($host === '') {
+ return "\0";
+ }
+
+ // break up domain name at each dot that is not preceeded by a backslash (escaped notation)
+ return $this->textsToBinary(
+ \array_map(
+ 'stripcslashes',
+ \preg_split(
+ '/(?<!\\\\)\./',
+ $host . '.'
+ )
+ )
+ );
+ }
+}