Skip to content

Commit c675821

Browse files
committed
[dns] Tests for resolver, related changes and refactorings
1 parent 5542f8e commit c675821

5 files changed

Lines changed: 103 additions & 29 deletions

File tree

BadServerException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace React\Dns;
4+
5+
class BadServerException extends \Exception
6+
{
7+
}

Model/Record.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@ class Record
99
public $class;
1010
public $ttl;
1111
public $data;
12+
13+
public function __construct($name, $type, $class, $ttl = 0, $data = null)
14+
{
15+
$this->name = $name;
16+
$this->type = $type;
17+
$this->class = $class;
18+
$this->ttl = $ttl;
19+
$this->data = $data;
20+
}
1221
}

Parser.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,9 @@ private function parseAnswer(Message $message)
174174

175175
$message->data = substr($message->data, $consumed) ?: '';
176176

177-
$record = new Record();
178-
$record->name = implode('.', $labels);
179-
$record->type = $type;
180-
$record->class = $class;
181-
$record->ttl = $this->signedLongToUnsignedLong($ttl);
182-
$record->data = $rdata;
177+
$name = implode('.', $labels);
178+
$ttl = $this->signedLongToUnsignedLong($ttl);
179+
$record = new Record($name, $type, $class, $ttl, $rdata);
183180

184181
$message->answers[] = $record;
185182

RecordNotFoundException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace React\Dns;
4+
5+
class RecordNotFoundException extends \Exception
6+
{
7+
}

Resolver.php

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,119 @@ class Resolver
1010
{
1111
private $nameserver;
1212
private $loop;
13+
private $parser;
14+
private $dumper;
1315

14-
public function __construct($nameserver, LoopInterface $loop)
16+
public function __construct($nameserver, LoopInterface $loop, Parser $parser = null, BinaryDumper $dumper = null)
1517
{
1618
$this->nameserver = $this->addPortToServerIfMissing($nameserver);
1719
$this->loop = $loop;
20+
$this->parser = $parser ?: new Parser();
21+
$this->dumper = $dumper ?: new BinaryDumper();
1822
}
1923

20-
public function resolve($domain, $callback)
24+
public function resolve($domain, $callback, $errback = null)
2125
{
26+
$that = $this;
27+
2228
$query = new Query($domain, Message::TYPE_A, Message::CLASS_IN);
2329

24-
$this->query($this->nameserver, $query, function (Message $response) use ($callback) {
25-
$answer = $response->answers[array_rand($response->answers)];
26-
$address = $answer->data;
27-
$callback($address);
30+
$this->query($this->nameserver, $query, function (Message $response) use ($that, $callback, $errback) {
31+
try {
32+
$answer = $that->pickRandomAnswerOfType($response, Message::TYPE_A);
33+
$address = $answer->data;
34+
$callback($address);
35+
} catch (RecordNotFoundException $e) {
36+
if (!$errback) {
37+
throw $e;
38+
}
39+
40+
$errback($e);
41+
}
2842
});
2943
}
3044

3145
public function query($nameserver, Query $query, $callback)
3246
{
33-
$dumper = new BinaryDumper();
34-
$parser = new Parser();
47+
$request = $this->prepareRequest($query);
48+
49+
$queryData = $this->dumper->toBinary($request);
50+
$transport = strlen($queryData) > 512 ? 'tcp' : 'udp';
51+
52+
$this->doQuery($nameserver, $transport, $queryData, $callback);
53+
}
3554

55+
public function pickRandomAnswerOfType(Message $response, $type)
56+
{
57+
$filteredAnswers = array_filter($response->answers, function ($answer) use ($type) {
58+
return $type === $answer->type;
59+
});
60+
61+
if (0 === count($filteredAnswers)) {
62+
$message = sprintf('DNS Request did not return valid answer. Received answers: %s', json_encode($response->answers));
63+
throw new RecordNotFoundException($message);
64+
}
65+
66+
$answer = $filteredAnswers[array_rand($filteredAnswers)];
67+
68+
return $answer;
69+
}
70+
71+
public function prepareRequest(Query $query)
72+
{
3673
$request = new Message();
3774
$request->header->set('id', rand());
3875
$request->header->set('rd', 1);
3976
$request->questions[] = (array) $query;
4077
$request->prepare();
4178

42-
$queryData = $dumper->toBinary($request);
43-
$transport = strlen($queryData) > 512 ? 'tcp' : 'udp';
44-
45-
$this->doQuery($nameserver, $transport, $queryData, $parser, $callback);
79+
return $request;
4680
}
4781

48-
public function doQuery($nameserver, $transport, $queryData, Parser $parser, $callback)
82+
public function doQuery($nameserver, $transport, $queryData, $callback)
4983
{
5084
$that = $this;
85+
$parser = $this->parser;
5186

5287
$response = new Message();
5388

54-
$fd = stream_socket_client("$transport://$nameserver");
55-
$conn = new Connection($fd, $this->loop);
56-
$conn->on('data', function ($data) use ($that, $conn, $parser, $response, $callback) {
89+
$retryWithTcp = function () use ($that, $nameserver, $queryData, $callback) {
90+
$that->doQuery($nameserver, 'tcp', $queryData, $callback);
91+
};
92+
93+
$conn = $this->createConnection($nameserver, $transport);
94+
$conn->on('data', function ($data) use ($that, $retryWithTcp, $conn, $parser, $response, $transport, $callback) {
5795
$responseReady = $parser->parseChunk($data, $response);
58-
if ($responseReady) {
59-
if ($response->header->isTruncated()) {
60-
$conn->end();
61-
$that->doQuery($nameserver, 'tcp', $queryData, $parser, $callback);
62-
return;
96+
97+
if (!$responseReady) {
98+
return;
99+
}
100+
101+
if ($response->header->isTruncated()) {
102+
if ('tcp' === $transport) {
103+
throw new BadServerException('The server set the truncated bit although we issued a TCP request');
63104
}
105+
64106
$conn->end();
65-
$callback($response);
107+
$retryWithTcp();
108+
return;
66109
}
110+
111+
$conn->end();
112+
$callback($response);
67113
});
68114
$conn->write($queryData);
69115
}
70116

71-
private function addPortToServerIfMissing($nameserver)
117+
protected function createConnection($nameserver, $transport)
118+
{
119+
$fd = stream_socket_client("$transport://$nameserver");
120+
$conn = new Connection($fd, $this->loop);
121+
122+
return $conn;
123+
}
124+
125+
protected function addPortToServerIfMissing($nameserver)
72126
{
73127
return false === strpos($nameserver, ':') ? "$nameserver:53" : $nameserver;
74128
}

0 commit comments

Comments
 (0)