@@ -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