| 1 |
liveuser |
1 |
# Dns
|
|
|
2 |
|
|
|
3 |
[](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
|