Skip to content

Commit b1453be

Browse files
committed
Use message properties instead of header bag attributes
1 parent daca439 commit b1453be

14 files changed

Lines changed: 147 additions & 183 deletions

src/Model/HeaderBag.php

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

src/Model/Message.php

Lines changed: 58 additions & 40 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,8 +44,8 @@ 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;
4550

4651
return $request;
@@ -56,11 +61,9 @@ public static function createRequestForQuery(Query $query)
5661
public static function createResponseWithAnswersForQuery(Query $query, array $answers)
5762
{
5863
$response = new Message();
59-
$response->header->set('id', self::generateId());
60-
$response->header->set('qr', 1);
61-
$response->header->set('opcode', Message::OPCODE_QUERY);
62-
$response->header->set('rd', 1);
63-
$response->header->set('rcode', Message::RCODE_OK);
64+
$response->id = self::generateId();
65+
$response->qr = true;
66+
$response->rd = true;
6467

6568
$response->questions[] = $query;
6669

@@ -98,9 +101,55 @@ private static function generateId()
98101
}
99102

100103
/**
101-
* @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
124+
*/
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
102145
*/
103-
public $header;
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;
104153

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

src/Protocol/BinaryDumper.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,19 @@ public function toBinary(Message $message)
3131
*/
3232
private function headerToBinary(Message $message)
3333
{
34-
$header = $message->header;
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

src/Protocol/Parser.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,14 @@ private function parse($data, Message $message)
4646
list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', substr($message->data, 0, 12)));
4747
$message->consumed += 12;
4848

49-
$message->header->set('id', $id);
50-
$message->header->set('rcode', $fields & bindec('1111'));
51-
$message->header->set('z', ($fields >> 4) & bindec('111'));
52-
$message->header->set('ra', ($fields >> 7) & 1);
53-
$message->header->set('rd', ($fields >> 8) & 1);
54-
$message->header->set('tc', ($fields >> 9) & 1);
55-
$message->header->set('aa', ($fields >> 10) & 1);
56-
$message->header->set('opcode', ($fields >> 11) & bindec('1111'));
57-
$message->header->set('qr', ($fields >> 15) & 1);
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;
5857

5958
// parse all questions
6059
for ($i = $qdCount; $i > 0; --$i) {

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

src/Query/UdpTransportExecutor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,15 @@ public function query($nameserver, Query $query)
160160

161161
// ignore and await next if we received an unexpected response ID
162162
// this may as well be a fake response from an attacker (possible cache poisoning)
163-
if ($response->getId() !== $request->getId()) {
163+
if ($response->id !== $request->id) {
164164
return;
165165
}
166166

167167
// we only react to the first valid message, so remove socket from loop and close
168168
$loop->removeReadStream($socket);
169169
\fclose($socket);
170170

171-
if ($response->header->isTruncated()) {
171+
if ($response->tc) {
172172
$deferred->reject(new \RuntimeException('DNS query for ' . $query->name . ' failed: The server returned a truncated result for a UDP query, but retrying via TCP is currently not supported'));
173173
return;
174174
}

src/Resolver/Resolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function resolveAll($domain, $type)
135135
public function extractValues(Query $query, Message $response)
136136
{
137137
// reject if response code indicates this is an error response message
138-
$code = $response->getResponseCode();
138+
$code = $response->rcode;
139139
if ($code !== Message::RCODE_OK) {
140140
switch ($code) {
141141
case Message::RCODE_FORMAT_ERROR:

tests/Model/MessageTest.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ public function testCreateRequestDesiresRecusion()
1313
$query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
1414
$request = Message::createRequestForQuery($query);
1515

16-
$this->assertTrue($request->header->isQuery());
17-
$this->assertSame(1, $request->header->get('rd'));
16+
$this->assertFalse($request->qr);
17+
$this->assertTrue($request->rd);
1818
}
1919

2020
public function testCreateResponseWithNoAnswers()
@@ -23,9 +23,7 @@ public function testCreateResponseWithNoAnswers()
2323
$answers = array();
2424
$request = Message::createResponseWithAnswersForQuery($query, $answers);
2525

26-
$this->assertFalse($request->header->isQuery());
27-
$this->assertTrue($request->header->isResponse());
28-
$this->assertEquals(0, $request->header->get('anCount'));
29-
$this->assertEquals(Message::RCODE_OK, $request->getResponseCode());
26+
$this->assertTrue($request->qr);
27+
$this->assertEquals(Message::RCODE_OK, $request->rcode);
3028
}
3129
}

tests/Protocol/BinaryDumperTest.php

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ public function testToBinaryRequestMessage()
2020
$expected = $this->formatHexDump($data);
2121

2222
$request = new Message();
23-
$request->header->set('id', 0x7262);
24-
$request->header->set('rd', 1);
23+
$request->id = 0x7262;
24+
$request->rd = true;
2525

2626
$request->questions[] = new Query(
2727
'igor.io',
@@ -48,8 +48,8 @@ public function testToBinaryRequestMessageWithCustomOptForEdns0()
4848
$expected = $this->formatHexDump($data);
4949

5050
$request = new Message();
51-
$request->header->set('id', 0x7262);
52-
$request->header->set('rd', 1);
51+
$request->id = 0x7262;
52+
$request->rd = true;
5353

5454
$request->questions[] = new Query(
5555
'igor.io',
@@ -76,9 +76,9 @@ public function testToBinaryResponseMessageWithoutRecords()
7676
$expected = $this->formatHexDump($data);
7777

7878
$response = new Message();
79-
$response->header->set('id', 0x7262);
80-
$response->header->set('rd', 1);
81-
$response->header->set('rcode', Message::RCODE_OK);
79+
$response->id = 0x7262;
80+
$response->rd = true;
81+
$response->rcode = Message::RCODE_OK;
8282

8383
$response->questions[] = new Query(
8484
'igor.io',
@@ -108,9 +108,9 @@ public function testToBinaryForResponseWithSRVRecord()
108108
$expected = $this->formatHexDump($data);
109109

110110
$response = new Message();
111-
$response->header->set('id', 0x7262);
112-
$response->header->set('rd', 1);
113-
$response->header->set('rcode', Message::RCODE_OK);
111+
$response->id = 0x7262;
112+
$response->rd = true;
113+
$response->rcode = Message::RCODE_OK;
114114

115115
$response->questions[] = new Query(
116116
'igor.io',
@@ -150,9 +150,9 @@ public function testToBinaryForResponseWithSOARecord()
150150
$expected = $this->formatHexDump($data);
151151

152152
$response = new Message();
153-
$response->header->set('id', 0x7262);
154-
$response->header->set('rd', 1);
155-
$response->header->set('rcode', Message::RCODE_OK);
153+
$response->id = 0x7262;
154+
$response->rd = true;
155+
$response->rcode = Message::RCODE_OK;
156156

157157
$response->questions[] = new Query(
158158
'igor.io',
@@ -199,9 +199,9 @@ public function testToBinaryForResponseWithMultipleAnswerRecords()
199199
$expected = $this->formatHexDump($data);
200200

201201
$response = new Message();
202-
$response->header->set('id', 0x7262);
203-
$response->header->set('rd', 1);
204-
$response->header->set('rcode', Message::RCODE_OK);
202+
$response->id = 0x7262;
203+
$response->rd = true;
204+
$response->rcode = Message::RCODE_OK;
205205

206206
$response->questions[] = new Query(
207207
'igor.io',
@@ -237,9 +237,9 @@ public function testToBinaryForResponseWithAnswerAndAdditionalRecord()
237237
$expected = $this->formatHexDump($data);
238238

239239
$response = new Message();
240-
$response->header->set('id', 0x7262);
241-
$response->header->set('rd', 1);
242-
$response->header->set('rcode', Message::RCODE_OK);
240+
$response->id = 0x7262;
241+
$response->rd = true;
242+
$response->rcode = Message::RCODE_OK;
243243

244244
$response->questions[] = new Query(
245245
'igor.io',

0 commit comments

Comments
 (0)