Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace HeaderBag attributes with simple Message properties #132

Merged
merged 2 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 0 additions & 54 deletions src/Model/HeaderBag.php

This file was deleted.

106 changes: 58 additions & 48 deletions src/Model/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

use React\Dns\Query\Query;

/**
* This class represents an outgoing query message or an incoming response message
*
* @link https://tools.ietf.org/html/rfc1035#section-4.1.1
*/
class Message
{
const TYPE_A = 1;
Expand Down Expand Up @@ -39,10 +44,9 @@ class Message
public static function createRequestForQuery(Query $query)
{
$request = new Message();
$request->header->set('id', self::generateId());
$request->header->set('rd', 1);
$request->id = self::generateId();
$request->rd = true;
$request->questions[] = $query;
$request->prepare();

return $request;
}
Expand All @@ -57,20 +61,16 @@ public static function createRequestForQuery(Query $query)
public static function createResponseWithAnswersForQuery(Query $query, array $answers)
{
$response = new Message();
$response->header->set('id', self::generateId());
$response->header->set('qr', 1);
$response->header->set('opcode', Message::OPCODE_QUERY);
$response->header->set('rd', 1);
$response->header->set('rcode', Message::RCODE_OK);
$response->id = self::generateId();
$response->qr = true;
$response->rd = true;

$response->questions[] = $query;

foreach ($answers as $record) {
$response->answers[] = $record;
}

$response->prepare();

return $response;
}

Expand Down Expand Up @@ -101,9 +101,55 @@ private static function generateId()
}

/**
* @var HeaderBag
* The 16 bit message ID
*
* The response message ID has to match the request message ID. This allows
* the receiver to verify this is the correct response message. An outside
* attacker may try to inject fake responses by "guessing" the message ID,
* so this should use a proper CSPRNG to avoid possible cache poisoning.
*
* @var int 16 bit message ID
* @see self::generateId()
*/
public $id = 0;

/**
* @var bool Query/Response flag, query=false or response=true
*/
public $qr = false;

/**
* @var int specifies the kind of query (4 bit), see self::OPCODE_* constants
* @see self::OPCODE_QUERY
*/
public $header;
public $opcode = self::OPCODE_QUERY;

/**
*
* @var bool Authoritative Answer
*/
public $aa = false;

/**
* @var bool TrunCation
*/
public $tc = false;

/**
* @var bool Recursion Desired
*/
public $rd = false;

/**
* @var bool Recursion Available
*/
public $ra = false;

/**
* @var int response code (4 bit), see self::RCODE_* constants
* @see self::RCODE_OK
*/
public $rcode = Message::RCODE_OK;

/**
* An array of Query objects
Expand Down Expand Up @@ -136,40 +182,4 @@ private static function generateId()
* @var Record[]
*/
public $additional = array();

public function __construct()
{
$this->header = new HeaderBag();
}

/**
* Returns the 16 bit message ID
*
* The response message ID has to match the request message ID. This allows
* the receiver to verify this is the correct response message. An outside
* attacker may try to inject fake responses by "guessing" the message ID,
* so this should use a proper CSPRNG to avoid possible cache poisoning.
*
* @return int
* @see self::generateId()
*/
public function getId()
{
return $this->header->get('id');
}

/**
* Returns the response code (RCODE)
*
* @return int see self::RCODE_* constants
*/
public function getResponseCode()
{
return $this->header->get('rcode');
}

public function prepare()
{
$this->header->populateCounts($this);
}
}
31 changes: 15 additions & 16 deletions src/Protocol/BinaryDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace React\Dns\Protocol;

use React\Dns\Model\HeaderBag;
use React\Dns\Model\Message;
use React\Dns\Model\Record;
use React\Dns\Query\Query;
Expand All @@ -17,7 +16,7 @@ public function toBinary(Message $message)
{
$data = '';

$data .= $this->headerToBinary($message->header);
$data .= $this->headerToBinary($message);
$data .= $this->questionToBinary($message->questions);
$data .= $this->recordsToBinary($message->answers);
$data .= $this->recordsToBinary($message->authority);
Expand All @@ -30,28 +29,28 @@ public function toBinary(Message $message)
* @param Message $message
* @return string
*/
private function headerToBinary(HeaderBag $header)
private function headerToBinary(Message $message)
{
$data = '';

$data .= pack('n', $header->get('id'));
$data .= pack('n', $message->id);

$flags = 0x00;
$flags = ($flags << 1) | $header->get('qr');
$flags = ($flags << 4) | $header->get('opcode');
$flags = ($flags << 1) | $header->get('aa');
$flags = ($flags << 1) | $header->get('tc');
$flags = ($flags << 1) | $header->get('rd');
$flags = ($flags << 1) | $header->get('ra');
$flags = ($flags << 3) | $header->get('z');
$flags = ($flags << 4) | $header->get('rcode');
$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', $header->get('qdCount'));
$data .= pack('n', $header->get('anCount'));
$data .= pack('n', $header->get('nsCount'));
$data .= pack('n', $header->get('arCount'));
$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;
}
Expand Down
74 changes: 28 additions & 46 deletions src/Protocol/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,34 @@ public function parseMessage($data)

private function parse($data, Message $message)
{
if (!$message->header->get('id')) {
if (!$this->parseHeader($message)) {
return;
}
if (!isset($message->data[12 - 1])) {
return;
}

if ($message->header->get('qdCount') != count($message->questions)) {
if (!$this->parseQuestion($message)) {
list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', substr($message->data, 0, 12)));
$message->consumed += 12;

$message->id = $id;
$message->rcode = $fields & 0xf;
$message->ra = (($fields >> 7) & 1) === 1;
$message->rd = (($fields >> 8) & 1) === 1;
$message->tc = (($fields >> 9) & 1) === 1;
$message->aa = (($fields >> 10) & 1) === 1;
$message->opcode = ($fields >> 11) & 0xf;
$message->qr = (($fields >> 15) & 1) === 1;

// parse all questions
for ($i = $qdCount; $i > 0; --$i) {
$question = $this->parseQuestion($message);
if ($question === null) {
return;
} else {
$message->questions[] = $question;
}
}

// parse all answer records
for ($i = $message->header->get('anCount'); $i > 0; --$i) {
for ($i = $anCount; $i > 0; --$i) {
$record = $this->parseRecord($message);
if ($record === null) {
return;
Expand All @@ -62,7 +76,7 @@ private function parse($data, Message $message)
}

// parse all authority records
for ($i = $message->header->get('nsCount'); $i > 0; --$i) {
for ($i = $nsCount; $i > 0; --$i) {
$record = $this->parseRecord($message);
if ($record === null) {
return;
Expand All @@ -72,7 +86,7 @@ private function parse($data, Message $message)
}

// parse all additional records
for ($i = $message->header->get('arCount'); $i > 0; --$i) {
for ($i = $arCount; $i > 0; --$i) {
$record = $this->parseRecord($message);
if ($record === null) {
return;
Expand All @@ -84,36 +98,10 @@ private function parse($data, Message $message)
return $message;
}

private function parseHeader(Message $message)
{
if (!isset($message->data[12 - 1])) {
return;
}

$header = substr($message->data, 0, 12);
$message->consumed += 12;

list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', $header));

$rcode = $fields & bindec('1111');
$z = ($fields >> 4) & bindec('111');
$ra = ($fields >> 7) & 1;
$rd = ($fields >> 8) & 1;
$tc = ($fields >> 9) & 1;
$aa = ($fields >> 10) & 1;
$opcode = ($fields >> 11) & bindec('1111');
$qr = ($fields >> 15) & 1;

$vars = compact('id', 'qdCount', 'anCount', 'nsCount', 'arCount',
'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', 'rcode');

foreach ($vars as $name => $value) {
$message->header->set($name, $value);
}

return $message;
}

/**
* @param Message $message
* @return ?Query
*/
private function parseQuestion(Message $message)
{
$consumed = $message->consumed;
Expand All @@ -129,17 +117,11 @@ private function parseQuestion(Message $message)

$message->consumed = $consumed;

$message->questions[] = new Query(
return new Query(
implode('.', $labels),
$type,
$class
);

if ($message->header->get('qdCount') != count($message->questions)) {
return $this->parseQuestion($message);
}

return $message;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Query/CachingExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function ($message) use ($nameserver, $query, $id, $cache, $executor, &$pending,
return $pending = $executor->query($nameserver, $query)->then(
function (Message $message) use ($cache, $id, $that) {
// DNS response message received => store in cache when not truncated and return
if (!$message->header->isTruncated()) {
if (!$message->tc) {
$cache->set($id, $message, $that->ttl($message));
}

Expand Down
Loading