22
33namespace React \Dns \Resolver ;
44
5+ use React \Dns \Model \Message ;
56use React \Dns \Query \ExecutorInterface ;
67use React \Dns \Query \Query ;
78use React \Dns \RecordNotFoundException ;
8- use React \Dns \Model \Message ;
99use React \Promise \PromiseInterface ;
1010
1111class Resolver
@@ -34,9 +34,11 @@ public function __construct($nameserver, ExecutorInterface $executor)
3434 *
3535 * If the DNS server sends a DNS response message that contains more than
3636 * one IP address for this query, it will randomly pick one of the IP
37- * addresses from the response.
37+ * addresses from the response. If you want the full list of IP addresses
38+ * or want to send a different type of query, you should use the
39+ * [`resolveAll()`](#resolveall) method instead.
3840 *
39- * If the DNS server sends a DNS response message that indicated an error
41+ * If the DNS server sends a DNS response message that indicates an error
4042 * code, this method will reject with a `RecordNotFoundException`. Its
4143 * message and code can be used to check for the response code.
4244 *
@@ -57,17 +59,90 @@ public function __construct($nameserver, ExecutorInterface $executor)
5759 */
5860 public function resolve ($ domain )
5961 {
60- $ query = new Query ($ domain , Message::TYPE_A , Message::CLASS_IN );
62+ return $ this ->resolveAll ($ domain , Message::TYPE_A )->then (function (array $ ips ) {
63+ return $ ips [array_rand ($ ips )];
64+ });
65+ }
66+
67+ /**
68+ * Resolves all record values for the given $domain name and query $type.
69+ *
70+ * ```php
71+ * $resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) {
72+ * echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
73+ * });
74+ *
75+ * $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) {
76+ * echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
77+ * });
78+ * ```
79+ *
80+ * This is one of the main methods in this package. It sends a DNS query
81+ * for the given $domain name to your DNS server and returns a list with all
82+ * record values on success.
83+ *
84+ * If the DNS server sends a DNS response message that contains one or more
85+ * records for this query, it will return a list with all record values
86+ * from the response. You can use the `Message::TYPE_*` constants to control
87+ * which type of query will be sent. Note that this method always returns a
88+ * list of record values, but each record value type depends on the query
89+ * type. For example, it returns the IPv4 addresses for type `A` queries,
90+ * the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`,
91+ * `CNAME` and `PTR` queries and structured data for other queries. See also
92+ * the `Record` documentation for more details.
93+ *
94+ * If the DNS server sends a DNS response message that indicates an error
95+ * code, this method will reject with a `RecordNotFoundException`. Its
96+ * message and code can be used to check for the response code.
97+ *
98+ * If the DNS communication fails and the server does not respond with a
99+ * valid response message, this message will reject with an `Exception`.
100+ *
101+ * Pending DNS queries can be cancelled by cancelling its pending promise like so:
102+ *
103+ * ```php
104+ * $promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA);
105+ *
106+ * $promise->cancel();
107+ * ```
108+ *
109+ * @param string $domain
110+ * @return PromiseInterface Returns a promise which resolves with all record values on success or
111+ * rejects with an Exception on error.
112+ */
113+ public function resolveAll ($ domain , $ type )
114+ {
115+ $ query = new Query ($ domain , $ type , Message::CLASS_IN );
61116 $ that = $ this ;
62117
63- return $ this ->executor
64- ->query ($ this ->nameserver , $ query )
65- ->then (function (Message $ response ) use ($ query , $ that ) {
66- return $ that ->extractAddress ($ query , $ response );
67- });
118+ return $ this ->executor ->query (
119+ $ this ->nameserver ,
120+ $ query
121+ )->then (function (Message $ response ) use ($ query , $ that ) {
122+ return $ that ->extractValues ($ query , $ response );
123+ });
68124 }
69125
126+ /**
127+ * @deprecated unused, exists for BC only
128+ */
70129 public function extractAddress (Query $ query , Message $ response )
130+ {
131+ $ addresses = $ this ->extractValues ($ query , $ response );
132+
133+ return $ addresses [array_rand ($ addresses )];
134+ }
135+
136+ /**
137+ * [Internal] extract all resource record values from response for this query
138+ *
139+ * @param Query $query
140+ * @param Message $response
141+ * @return array
142+ * @throws RecordNotFoundException when response indicates an error or contains no data
143+ * @internal
144+ */
145+ public function extractValues (Query $ query , Message $ response )
71146 {
72147 // reject if response code indicates this is an error response message
73148 $ code = $ response ->getResponseCode ();
@@ -98,7 +173,7 @@ public function extractAddress(Query $query, Message $response)
98173 }
99174
100175 $ answers = $ response ->answers ;
101- $ addresses = $ this ->resolveAliases ($ answers , $ query ->name );
176+ $ addresses = $ this ->valuesByNameAndType ($ answers , $ query ->name , $ query -> type );
102177
103178 // reject if we did not receive a valid answer (domain is valid, but no record for this type could be found)
104179 if (0 === count ($ addresses )) {
@@ -107,36 +182,45 @@ public function extractAddress(Query $query, Message $response)
107182 );
108183 }
109184
110- $ address = $ addresses [array_rand ($ addresses )];
111- return $ address ;
185+ return array_values ($ addresses );
112186 }
113187
188+ /**
189+ * @deprecated unused, exists for BC only
190+ */
114191 public function resolveAliases (array $ answers , $ name )
115192 {
116- $ named = $ this ->filterByName ($ answers , $ name );
117- $ aRecords = $ this ->filterByType ($ named , Message::TYPE_A );
118- $ cnameRecords = $ this ->filterByType ($ named , Message::TYPE_CNAME );
193+ return $ this ->valuesByNameAndType ($ answers , $ name , Message::TYPE_A );
194+ }
119195
120- if ($ aRecords ) {
121- return $ this ->mapRecordData ($ aRecords );
196+ /**
197+ * @param \React\Dns\Model\Record[] $answers
198+ * @param string $name
199+ * @param int $type
200+ * @return array
201+ */
202+ private function valuesByNameAndType (array $ answers , $ name , $ type )
203+ {
204+ // return all record values for this name and type (if any)
205+ $ named = $ this ->filterByName ($ answers , $ name );
206+ $ records = $ this ->filterByType ($ named , $ type );
207+ if ($ records ) {
208+ return $ this ->mapRecordData ($ records );
122209 }
123210
211+ // no matching records found? check if there are any matching CNAMEs instead
212+ $ cnameRecords = $ this ->filterByType ($ named , Message::TYPE_CNAME );
124213 if ($ cnameRecords ) {
125- $ aRecords = array ();
126-
127214 $ cnames = $ this ->mapRecordData ($ cnameRecords );
128215 foreach ($ cnames as $ cname ) {
129- $ targets = $ this ->filterByName ($ answers , $ cname );
130- $ aRecords = array_merge (
131- $ aRecords ,
132- $ this ->resolveAliases ($ answers , $ cname )
216+ $ records = array_merge (
217+ $ records ,
218+ $ this ->valuesByNameAndType ($ answers , $ cname , $ type )
133219 );
134220 }
135-
136- return $ aRecords ;
137221 }
138222
139- return array () ;
223+ return $ records ;
140224 }
141225
142226 private function filterByName (array $ answers , $ name )
0 commit comments