Subversion Repositories qbpwcf-lib(archive)

Rev

Rev 915 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 liveuser 1
# Dns
2
 
3
[![Build Status](https://travis-ci.org/reactphp/dns.svg?branch=master)](https://travis-ci.org/reactphp/dns)
4
 
5
Async DNS resolver for [ReactPHP](https://reactphp.org/).
6
 
7
The main point of the DNS component is to provide async DNS resolution.
8
However, it is really a toolkit for working with DNS messages, and could
9
easily be used to create a DNS server.
10
 
11
**Table of contents**
12
 
13
* [Basic usage](#basic-usage)
14
* [Caching](#caching)
15
  * [Custom cache adapter](#custom-cache-adapter)
16
* [ResolverInterface](#resolverinterface)
17
  * [resolve()](#resolve)
18
  * [resolveAll()](#resolveall)
19
* [Advanced usage](#advanced-usage)
20
  * [UdpTransportExecutor](#udptransportexecutor)
21
  * [TcpTransportExecutor](#tcptransportexecutor)
22
  * [SelectiveTransportExecutor](#selectivetransportexecutor)
23
  * [HostsFileExecutor](#hostsfileexecutor)
24
* [Install](#install)
25
* [Tests](#tests)
26
* [License](#license)
27
* [References](#references)
28
 
29
## Basic usage
30
 
31
The most basic usage is to just create a resolver through the resolver
32
factory. All you need to give it is a nameserver, then you can start resolving
33
names, baby!
34
 
35
```php
36
$loop = React\EventLoop\Factory::create();
37
 
38
$config = React\Dns\Config\Config::loadSystemConfigBlocking();
39
$server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
40
 
41
$factory = new React\Dns\Resolver\Factory();
42
$dns = $factory->create($server, $loop);
43
 
44
$dns->resolve('igor.io')->then(function ($ip) {
45
    echo "Host: $ip\n";
46
});
47
 
48
$loop->run();
49
```
50
 
51
See also the [first example](examples).
52
 
53
The `Config` class can be used to load the system default config. This is an
54
operation that may access the filesystem and block. Ideally, this method should
55
thus be executed only once before the loop starts and not repeatedly while it is
56
running.
57
Note that this class may return an *empty* configuration if the system config
58
can not be loaded. As such, you'll likely want to apply a default nameserver
59
as above if none can be found.
60
 
61
> Note that the factory loads the hosts file from the filesystem once when
62
  creating the resolver instance.
63
  Ideally, this method should thus be executed only once before the loop starts
64
  and not repeatedly while it is running.
65
 
66
But there's more.
67
 
68
## Caching
69
 
70
You can cache results by configuring the resolver to use a `CachedExecutor`:
71
 
72
```php
73
$loop = React\EventLoop\Factory::create();
74
 
75
$config = React\Dns\Config\Config::loadSystemConfigBlocking();
76
$server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8';
77
 
78
$factory = new React\Dns\Resolver\Factory();
79
$dns = $factory->createCached($server, $loop);
80
 
81
$dns->resolve('igor.io')->then(function ($ip) {
82
    echo "Host: $ip\n";
83
});
84
 
85
...
86
 
87
$dns->resolve('igor.io')->then(function ($ip) {
88
    echo "Host: $ip\n";
89
});
90
 
91
$loop->run();
92
```
93
 
94
If the first call returns before the second, only one query will be executed.
95
The second result will be served from an in memory cache.
96
This is particularly useful for long running scripts where the same hostnames
97
have to be looked up multiple times.
98
 
99
See also the [third example](examples).
100
 
101
### Custom cache adapter
102
 
103
By default, the above will use an in memory cache.
104
 
105
You can also specify a custom cache implementing [`CacheInterface`](https://github.com/reactphp/cache) to handle the record cache instead:
106
 
107
```php
108
$cache = new React\Cache\ArrayCache();
109
$loop = React\EventLoop\Factory::create();
110
$factory = new React\Dns\Resolver\Factory();
111
$dns = $factory->createCached('8.8.8.8', $loop, $cache);
112
```
113
 
114
See also the wiki for possible [cache implementations](https://github.com/reactphp/react/wiki/Users#cache-implementations).
115
 
116
## ResolverInterface
117
 
118
<a id="resolver"><!-- legacy reference --></a>
119
 
120
### resolve()
121
 
122
The `resolve(string $domain): PromiseInterface<string,Exception>` method can be used to
123
resolve the given $domain name to a single IPv4 address (type `A` query).
124
 
125
```php
126
$resolver->resolve('reactphp.org')->then(function ($ip) {
127
    echo 'IP for reactphp.org is ' . $ip . PHP_EOL;
128
});
129
```
130
 
131
This is one of the main methods in this package. It sends a DNS query
132
for the given $domain name to your DNS server and returns a single IP
133
address on success.
134
 
135
If the DNS server sends a DNS response message that contains more than
136
one IP address for this query, it will randomly pick one of the IP
137
addresses from the response. If you want the full list of IP addresses
138
or want to send a different type of query, you should use the
139
[`resolveAll()`](#resolveall) method instead.
140
 
141
If the DNS server sends a DNS response message that indicates an error
142
code, this method will reject with a `RecordNotFoundException`. Its
143
message and code can be used to check for the response code.
144
 
145
If the DNS communication fails and the server does not respond with a
146
valid response message, this message will reject with an `Exception`.
147
 
148
Pending DNS queries can be cancelled by cancelling its pending promise like so:
149
 
150
```php
151
$promise = $resolver->resolve('reactphp.org');
152
 
153
$promise->cancel();
154
```
155
 
156
### resolveAll()
157
 
158
The `resolveAll(string $host, int $type): PromiseInterface<array,Exception>` method can be used to
159
resolve all record values for the given $domain name and query $type.
160
 
161
```php
162
$resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) {
163
    echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
164
});
165
 
166
$resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) {
167
    echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL;
168
});
169
```
170
 
171
This is one of the main methods in this package. It sends a DNS query
172
for the given $domain name to your DNS server and returns a list with all
173
record values on success.
174
 
175
If the DNS server sends a DNS response message that contains one or more
176
records for this query, it will return a list with all record values
177
from the response. You can use the `Message::TYPE_*` constants to control
178
which type of query will be sent. Note that this method always returns a
179
list of record values, but each record value type depends on the query
180
type. For example, it returns the IPv4 addresses for type `A` queries,
181
the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`,
182
`CNAME` and `PTR` queries and structured data for other queries. See also
183
the `Record` documentation for more details.
184
 
185
If the DNS server sends a DNS response message that indicates an error
186
code, this method will reject with a `RecordNotFoundException`. Its
187
message and code can be used to check for the response code.
188
 
189
If the DNS communication fails and the server does not respond with a
190
valid response message, this message will reject with an `Exception`.
191
 
192
Pending DNS queries can be cancelled by cancelling its pending promise like so:
193
 
194
```php
195
$promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA);
196
 
197
$promise->cancel();
198
```
199
 
200
## Advanced Usage
201
 
202
### UdpTransportExecutor
203
 
204
The `UdpTransportExecutor` can be used to
205
send DNS queries over a UDP transport.
206
 
207
This is the main class that sends a DNS query to your DNS server and is used
208
internally by the `Resolver` for the actual message transport.
209
 
210
For more advanced usages one can utilize this class directly.
211
The following example looks up the `IPv6` address for `igor.io`.
212
 
213
```php
214
$loop = Factory::create();
215
$executor = new UdpTransportExecutor('8.8.8.8:53', $loop);
216
 
217
$executor->query(
218
    new Query($name, Message::TYPE_AAAA, Message::CLASS_IN)
219
)->then(function (Message $message) {
220
    foreach ($message->answers as $answer) {
221
        echo 'IPv6: ' . $answer->data . PHP_EOL;
222
    }
223
}, 'printf');
224
 
225
$loop->run();
226
```
227
 
228
See also the [fourth example](examples).
229
 
230
Note that this executor does not implement a timeout, so you will very likely
231
want to use this in combination with a `TimeoutExecutor` like this:
232
 
233
```php
234
$executor = new TimeoutExecutor(
235
    new UdpTransportExecutor($nameserver, $loop),
236
    3.0,
237
    $loop
238
);
239
```
240
 
241
Also note that this executor uses an unreliable UDP transport and that it
242
does not implement any retry logic, so you will likely want to use this in
243
combination with a `RetryExecutor` like this:
244
 
245
```php
246
$executor = new RetryExecutor(
247
    new TimeoutExecutor(
248
        new UdpTransportExecutor($nameserver, $loop),
249
        3.0,
250
        $loop
251
    )
252
);
253
```
254
 
255
Note that this executor is entirely async and as such allows you to execute
256
any number of queries concurrently. You should probably limit the number of
257
concurrent queries in your application or you're very likely going to face
258
rate limitations and bans on the resolver end. For many common applications,
259
you may want to avoid sending the same query multiple times when the first
260
one is still pending, so you will likely want to use this in combination with
261
a `CoopExecutor` like this:
262
 
263
```php
264
$executor = new CoopExecutor(
265
    new RetryExecutor(
266
        new TimeoutExecutor(
267
            new UdpTransportExecutor($nameserver, $loop),
268
            3.0,
269
            $loop
270
        )
271
    )
272
);
273
```
274
 
275
> Internally, this class uses PHP's UDP sockets and does not take advantage
276
  of [react/datagram](https://github.com/reactphp/datagram) purely for
277
  organizational reasons to avoid a cyclic dependency between the two
278
  packages. Higher-level components should take advantage of the Datagram
279
  component instead of reimplementing this socket logic from scratch.
280
 
281
### TcpTransportExecutor
282
 
283
The `TcpTransportExecutor` class can be used to
284
send DNS queries over a TCP/IP stream transport.
285
 
286
This is one of the main classes that send a DNS query to your DNS server.
287
 
288
For more advanced usages one can utilize this class directly.
289
The following example looks up the `IPv6` address for `reactphp.org`.
290
 
291
```php
292
$loop = Factory::create();
293
$executor = new TcpTransportExecutor('8.8.8.8:53', $loop);
294
 
295
$executor->query(
296
    new Query($name, Message::TYPE_AAAA, Message::CLASS_IN)
297
)->then(function (Message $message) {
298
    foreach ($message->answers as $answer) {
299
        echo 'IPv6: ' . $answer->data . PHP_EOL;
300
    }
301
}, 'printf');
302
 
303
$loop->run();
304
```
305
 
306
See also [example #92](examples).
307
 
308
Note that this executor does not implement a timeout, so you will very likely
309
want to use this in combination with a `TimeoutExecutor` like this:
310
 
311
```php
312
$executor = new TimeoutExecutor(
313
    new TcpTransportExecutor($nameserver, $loop),
314
    3.0,
315
    $loop
316
);
317
```
318
 
319
Unlike the `UdpTransportExecutor`, this class uses a reliable TCP/IP
320
transport, so you do not necessarily have to implement any retry logic.
321
 
322
Note that this executor is entirely async and as such allows you to execute
323
queries concurrently. The first query will establish a TCP/IP socket
324
connection to the DNS server which will be kept open for a short period.
325
Additional queries will automatically reuse this existing socket connection
326
to the DNS server, will pipeline multiple requests over this single
327
connection and will keep an idle connection open for a short period. The
328
initial TCP/IP connection overhead may incur a slight delay if you only send
329
occasional queries – when sending a larger number of concurrent queries over
330
an existing connection, it becomes increasingly more efficient and avoids
331
creating many concurrent sockets like the UDP-based executor. You may still
332
want to limit the number of (concurrent) queries in your application or you
333
may be facing rate limitations and bans on the resolver end. For many common
334
applications, you may want to avoid sending the same query multiple times
335
when the first one is still pending, so you will likely want to use this in
336
combination with a `CoopExecutor` like this:
337
 
338
```php
339
$executor = new CoopExecutor(
340
    new TimeoutExecutor(
341
        new TcpTransportExecutor($nameserver, $loop),
342
        3.0,
343
        $loop
344
    )
345
);
346
```
347
 
348
> Internally, this class uses PHP's TCP/IP sockets and does not take advantage
349
  of [react/socket](https://github.com/reactphp/socket) purely for
350
  organizational reasons to avoid a cyclic dependency between the two
351
  packages. Higher-level components should take advantage of the Socket
352
  component instead of reimplementing this socket logic from scratch.
353
 
354
### SelectiveTransportExecutor
355
 
356
The `SelectiveTransportExecutor` class can be used to
357
Send DNS queries over a UDP or TCP/IP stream transport.
358
 
359
This class will automatically choose the correct transport protocol to send
360
a DNS query to your DNS server. It will always try to send it over the more
361
efficient UDP transport first. If this query yields a size related issue
362
(truncated messages), it will retry over a streaming TCP/IP transport.
363
 
364
For more advanced usages one can utilize this class directly.
365
The following example looks up the `IPv6` address for `reactphp.org`.
366
 
367
```php
368
$executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor);
369
 
370
$executor->query(
371
    new Query($name, Message::TYPE_AAAA, Message::CLASS_IN)
372
)->then(function (Message $message) {
373
    foreach ($message->answers as $answer) {
374
        echo 'IPv6: ' . $answer->data . PHP_EOL;
375
    }
376
}, 'printf');
377
```
378
 
379
Note that this executor only implements the logic to select the correct
380
transport for the given DNS query. Implementing the correct transport logic,
381
implementing timeouts and any retry logic is left up to the given executors,
382
see also [`UdpTransportExecutor`](#udptransportexecutor) and
383
[`TcpTransportExecutor`](#tcptransportexecutor) for more details.
384
 
385
Note that this executor is entirely async and as such allows you to execute
386
any number of queries concurrently. You should probably limit the number of
387
concurrent queries in your application or you're very likely going to face
388
rate limitations and bans on the resolver end. For many common applications,
389
you may want to avoid sending the same query multiple times when the first
390
one is still pending, so you will likely want to use this in combination with
391
a `CoopExecutor` like this:
392
 
393
```php
394
$executor = new CoopExecutor(
395
    new SelectiveTransportExecutor(
396
        $datagramExecutor,
397
        $streamExecutor
398
    )
399
);
400
```
401
 
402
### HostsFileExecutor
403
 
404
Note that the above `UdpTransportExecutor` class always performs an actual DNS query.
405
If you also want to take entries from your hosts file into account, you may
406
use this code:
407
 
408
```php
409
$hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking();
410
 
411
$executor = new UdpTransportExecutor('8.8.8.8:53', $loop);
412
$executor = new HostsFileExecutor($hosts, $executor);
413
 
414
$executor->query(
415
    new Query('localhost', Message::TYPE_A, Message::CLASS_IN)
416
);
417
```
418
 
419
## Install
420
 
421
The recommended way to install this library is [through Composer](https://getcomposer.org).
422
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
423
 
424
This project follows [SemVer](https://semver.org/).
425
This will install the latest supported version:
426
 
427
```bash
428
$ composer require react/dns:^1.4
429
```
430
 
431
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
432
 
433
This project aims to run on any platform and thus does not require any PHP
434
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
435
HHVM.
436
It's *highly recommended to use PHP 7+* for this project.
437
 
438
## Tests
439
 
440
To run the test suite, you first need to clone this repo and then install all
441
dependencies [through Composer](https://getcomposer.org):
442
 
443
```bash
444
$ composer install
445
```
446
 
447
To run the test suite, go to the project root and run:
448
 
449
```bash
450
$ php vendor/bin/phpunit
451
```
452
 
453
The test suite also contains a number of functional integration tests that rely
454
on a stable internet connection.
455
If you do not want to run these, they can simply be skipped like this:
456
 
457
```bash
458
$ php vendor/bin/phpunit --exclude-group internet
459
```
460
 
461
## License
462
 
463
MIT, see [LICENSE file](LICENSE).
464
 
465
## References
466
 
467
* [RFC 1034](https://tools.ietf.org/html/rfc1034) Domain Names - Concepts and Facilities
468
* [RFC 1035](https://tools.ietf.org/html/rfc1035) Domain Names - Implementation and Specification