Skip to content

Commit 1b331dd

Browse files
authored
Merge pull request #132 from clue-labs/message-properties
Replace `HeaderBag` attributes with simple `Message` properties
2 parents 3d636a5 + b1453be commit 1b331dd

14 files changed

Lines changed: 173 additions & 274 deletions

src/Model/HeaderBag.php

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/Model/Message.php

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
use React\Dns\Query\Query;
66

7+
/**
8+
* This class represents an outgoing query message or an incoming response message
9+
*
10+
* @link https://tools.ietf.org/html/rfc1035#section-4.1.1
11+
*/
712
class Message
813
{
914
const TYPE_A = 1;
@@ -39,10 +44,9 @@ class Message
3944
public static function createRequestForQuery(Query $query)
4045
{
4146
$request = new Message();
42-
$request->header->set('id', self::generateId());
43-
$request->header->set('rd', 1);
47+
$request->id = self::generateId();
48+
$request->rd = true;
4449
$request->questions[] = $query;
45-
$request->prepare();
4650

4751
return $request;
4852
}
@@ -57,20 +61,16 @@ public static function createRequestForQuery(Query $query)
5761
public static function createResponseWithAnswersForQuery(Query $query, array $answers)
5862
{
5963
$response = new Message();
60-
$response->header->set('id', self::generateId());
61-
$response->header->set('qr', 1);
62-
$response->header->set('opcode', Message::OPCODE_QUERY);
63-
$response->header->set('rd', 1);
64-
$response->header->set('rcode', Message::RCODE_OK);
64+
$response->id = self::generateId();
65+
$response->qr = true;
66+
$response->rd = true;
6567

6668
$response->questions[] = $query;
6769

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

72-
$response->prepare();
73-
7474
return $response;
7575
}
7676

@@ -101,9 +101,55 @@ private static function generateId()
101101
}
102102

103103
/**
104-
* @var HeaderBag
104+
* The 16 bit message ID
105+
*
106+
* The response message ID has to match the request message ID. This allows
107+
* the receiver to verify this is the correct response message. An outside
108+
* attacker may try to inject fake responses by "guessing" the message ID,
109+
* so this should use a proper CSPRNG to avoid possible cache poisoning.
110+
*
111+
* @var int 16 bit message ID
112+
* @see self::generateId()
113+
*/
114+
public $id = 0;
115+
116+
/**
117+
* @var bool Query/Response flag, query=false or response=true
118+
*/
119+
public $qr = false;
120+
121+
/**
122+
* @var int specifies the kind of query (4 bit), see self::OPCODE_* constants
123+
* @see self::OPCODE_QUERY
105124
*/
106-
public $header;
125+
public $opcode = self::OPCODE_QUERY;
126+
127+
/**
128+
*
129+
* @var bool Authoritative Answer
130+
*/
131+
public $aa = false;
132+
133+
/**
134+
* @var bool TrunCation
135+
*/
136+
public $tc = false;
137+
138+
/**
139+
* @var bool Recursion Desired
140+
*/
141+
public $rd = false;
142+
143+
/**
144+
* @var bool Recursion Available
145+
*/
146+
public $ra = false;
147+
148+
/**
149+
* @var int response code (4 bit), see self::RCODE_* constants
150+
* @see self::RCODE_OK
151+
*/
152+
public $rcode = Message::RCODE_OK;
107153

108154
/**
109155
* An array of Query objects
@@ -136,40 +182,4 @@ private static function generateId()
136182
* @var Record[]
137183
*/
138184
public $additional = array();
139-
140-
public function __construct()
141-
{
142-
$this->header = new HeaderBag();
143-
}
144-
145-
/**
146-
* Returns the 16 bit message ID
147-
*
148-
* The response message ID has to match the request message ID. This allows
149-
* the receiver to verify this is the correct response message. An outside
150-
* attacker may try to inject fake responses by "guessing" the message ID,
151-
* so this should use a proper CSPRNG to avoid possible cache poisoning.
152-
*
153-
* @return int
154-
* @see self::generateId()
155-
*/
156-
public function getId()
157-
{
158-
return $this->header->get('id');
159-
}
160-
161-
/**
162-
* Returns the response code (RCODE)
163-
*
164-
* @return int see self::RCODE_* constants
165-
*/
166-
public function getResponseCode()
167-
{
168-
return $this->header->get('rcode');
169-
}
170-
171-
public function prepare()
172-
{
173-
$this->header->populateCounts($this);
174-
}
175185
}

src/Protocol/BinaryDumper.php

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace React\Dns\Protocol;
44

5-
use React\Dns\Model\HeaderBag;
65
use React\Dns\Model\Message;
76
use React\Dns\Model\Record;
87
use React\Dns\Query\Query;
@@ -17,7 +16,7 @@ public function toBinary(Message $message)
1716
{
1817
$data = '';
1918

20-
$data .= $this->headerToBinary($message->header);
19+
$data .= $this->headerToBinary($message);
2120
$data .= $this->questionToBinary($message->questions);
2221
$data .= $this->recordsToBinary($message->answers);
2322
$data .= $this->recordsToBinary($message->authority);
@@ -30,28 +29,28 @@ public function toBinary(Message $message)
3029
* @param Message $message
3130
* @return string
3231
*/
33-
private function headerToBinary(HeaderBag $header)
32+
private function headerToBinary(Message $message)
3433
{
3534
$data = '';
3635

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

3938
$flags = 0x00;
40-
$flags = ($flags << 1) | $header->get('qr');
41-
$flags = ($flags << 4) | $header->get('opcode');
42-
$flags = ($flags << 1) | $header->get('aa');
43-
$flags = ($flags << 1) | $header->get('tc');
44-
$flags = ($flags << 1) | $header->get('rd');
45-
$flags = ($flags << 1) | $header->get('ra');
46-
$flags = ($flags << 3) | $header->get('z');
47-
$flags = ($flags << 4) | $header->get('rcode');
39+
$flags = ($flags << 1) | ($message->qr ? 1 : 0);
40+
$flags = ($flags << 4) | $message->opcode;
41+
$flags = ($flags << 1) | ($message->aa ? 1 : 0);
42+
$flags = ($flags << 1) | ($message->tc ? 1 : 0);
43+
$flags = ($flags << 1) | ($message->rd ? 1 : 0);
44+
$flags = ($flags << 1) | ($message->ra ? 1 : 0);
45+
$flags = ($flags << 3) | 0; // skip unused zero bit
46+
$flags = ($flags << 4) | $message->rcode;
4847

4948
$data .= pack('n', $flags);
5049

51-
$data .= pack('n', $header->get('qdCount'));
52-
$data .= pack('n', $header->get('anCount'));
53-
$data .= pack('n', $header->get('nsCount'));
54-
$data .= pack('n', $header->get('arCount'));
50+
$data .= pack('n', count($message->questions));
51+
$data .= pack('n', count($message->answers));
52+
$data .= pack('n', count($message->authority));
53+
$data .= pack('n', count($message->additional));
5554

5655
return $data;
5756
}

src/Protocol/Parser.php

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,34 @@ public function parseMessage($data)
3939

4040
private function parse($data, Message $message)
4141
{
42-
if (!$message->header->get('id')) {
43-
if (!$this->parseHeader($message)) {
44-
return;
45-
}
42+
if (!isset($message->data[12 - 1])) {
43+
return;
4644
}
4745

48-
if ($message->header->get('qdCount') != count($message->questions)) {
49-
if (!$this->parseQuestion($message)) {
46+
list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', substr($message->data, 0, 12)));
47+
$message->consumed += 12;
48+
49+
$message->id = $id;
50+
$message->rcode = $fields & 0xf;
51+
$message->ra = (($fields >> 7) & 1) === 1;
52+
$message->rd = (($fields >> 8) & 1) === 1;
53+
$message->tc = (($fields >> 9) & 1) === 1;
54+
$message->aa = (($fields >> 10) & 1) === 1;
55+
$message->opcode = ($fields >> 11) & 0xf;
56+
$message->qr = (($fields >> 15) & 1) === 1;
57+
58+
// parse all questions
59+
for ($i = $qdCount; $i > 0; --$i) {
60+
$question = $this->parseQuestion($message);
61+
if ($question === null) {
5062
return;
63+
} else {
64+
$message->questions[] = $question;
5165
}
5266
}
5367

5468
// parse all answer records
55-
for ($i = $message->header->get('anCount'); $i > 0; --$i) {
69+
for ($i = $anCount; $i > 0; --$i) {
5670
$record = $this->parseRecord($message);
5771
if ($record === null) {
5872
return;
@@ -62,7 +76,7 @@ private function parse($data, Message $message)
6276
}
6377

6478
// parse all authority records
65-
for ($i = $message->header->get('nsCount'); $i > 0; --$i) {
79+
for ($i = $nsCount; $i > 0; --$i) {
6680
$record = $this->parseRecord($message);
6781
if ($record === null) {
6882
return;
@@ -72,7 +86,7 @@ private function parse($data, Message $message)
7286
}
7387

7488
// parse all additional records
75-
for ($i = $message->header->get('arCount'); $i > 0; --$i) {
89+
for ($i = $arCount; $i > 0; --$i) {
7690
$record = $this->parseRecord($message);
7791
if ($record === null) {
7892
return;
@@ -84,36 +98,10 @@ private function parse($data, Message $message)
8498
return $message;
8599
}
86100

87-
private function parseHeader(Message $message)
88-
{
89-
if (!isset($message->data[12 - 1])) {
90-
return;
91-
}
92-
93-
$header = substr($message->data, 0, 12);
94-
$message->consumed += 12;
95-
96-
list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', $header));
97-
98-
$rcode = $fields & bindec('1111');
99-
$z = ($fields >> 4) & bindec('111');
100-
$ra = ($fields >> 7) & 1;
101-
$rd = ($fields >> 8) & 1;
102-
$tc = ($fields >> 9) & 1;
103-
$aa = ($fields >> 10) & 1;
104-
$opcode = ($fields >> 11) & bindec('1111');
105-
$qr = ($fields >> 15) & 1;
106-
107-
$vars = compact('id', 'qdCount', 'anCount', 'nsCount', 'arCount',
108-
'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', 'rcode');
109-
110-
foreach ($vars as $name => $value) {
111-
$message->header->set($name, $value);
112-
}
113-
114-
return $message;
115-
}
116-
101+
/**
102+
* @param Message $message
103+
* @return ?Query
104+
*/
117105
private function parseQuestion(Message $message)
118106
{
119107
$consumed = $message->consumed;
@@ -129,17 +117,11 @@ private function parseQuestion(Message $message)
129117

130118
$message->consumed = $consumed;
131119

132-
$message->questions[] = new Query(
120+
return new Query(
133121
implode('.', $labels),
134122
$type,
135123
$class
136124
);
137-
138-
if ($message->header->get('qdCount') != count($message->questions)) {
139-
return $this->parseQuestion($message);
140-
}
141-
142-
return $message;
143125
}
144126

145127
/**

src/Query/CachingExecutor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function ($message) use ($nameserver, $query, $id, $cache, $executor, &$pending,
4444
return $pending = $executor->query($nameserver, $query)->then(
4545
function (Message $message) use ($cache, $id, $that) {
4646
// DNS response message received => store in cache when not truncated and return
47-
if (!$message->header->isTruncated()) {
47+
if (!$message->tc) {
4848
$cache->set($id, $message, $that->ttl($message));
4949
}
5050

0 commit comments

Comments
 (0)