Async DNS over TCP (#25690)
This commit is contained in:
parent
d71ba251ed
commit
23b7f86a06
22 changed files with 848 additions and 108 deletions
50
akka-actor-tests/src/test/bind/etc/bind.keys
Executable file
50
akka-actor-tests/src/test/bind/etc/bind.keys
Executable file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# The bind.keys file is used to override the built-in DNSSEC trust anchors
|
||||||
|
# which are included as part of BIND 9. The only trust anchors it contains
|
||||||
|
# are for the DNS root zone ("."). Trust anchors for any other zones MUST
|
||||||
|
# be configured elsewhere; if they are configured here, they will not be
|
||||||
|
# recognized or used by named.
|
||||||
|
#
|
||||||
|
# The built-in trust anchors are provided for convenience of configuration.
|
||||||
|
# They are not activated within named.conf unless specifically switched on.
|
||||||
|
# To use the built-in key, use "dnssec-validation auto;" in the
|
||||||
|
# named.conf options. Without this option being set, the keys in this
|
||||||
|
# file are ignored.
|
||||||
|
#
|
||||||
|
# This file is NOT expected to be user-configured.
|
||||||
|
#
|
||||||
|
# These keys are current as of October 2017. If any key fails to
|
||||||
|
# initialize correctly, it may have expired. In that event you should
|
||||||
|
# replace this file with a current version. The latest version of
|
||||||
|
# bind.keys can always be obtained from ISC at https://www.isc.org/bind-keys.
|
||||||
|
#
|
||||||
|
# See https://data.iana.org/root-anchors/root-anchors.xml
|
||||||
|
# for current trust anchor information for the root zone.
|
||||||
|
|
||||||
|
managed-keys {
|
||||||
|
# This key (19036) is to be phased out starting in 2017. It will
|
||||||
|
# remain in the root zone for some time after its successor key
|
||||||
|
# has been added. It will remain this file until it is removed from
|
||||||
|
# the root zone.
|
||||||
|
. initial-key 257 3 8 "AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF
|
||||||
|
FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX
|
||||||
|
bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD
|
||||||
|
X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz
|
||||||
|
W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS
|
||||||
|
Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq
|
||||||
|
QxA+Uk1ihz0=";
|
||||||
|
|
||||||
|
# This key (20326) was published in the root zone in 2017.
|
||||||
|
# Servers which were already using the old key (19036) should
|
||||||
|
# roll seamlessly to this new one via RFC 5011 rollover. Servers
|
||||||
|
# being set up for the first time can use the contents of this
|
||||||
|
# file as initializing keys; thereafter, the keys in the
|
||||||
|
# managed key database will be trusted and maintained
|
||||||
|
# automatically.
|
||||||
|
. initial-key 257 3 8 "AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3
|
||||||
|
+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kv
|
||||||
|
ArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF
|
||||||
|
0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+e
|
||||||
|
oZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfd
|
||||||
|
RUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwN
|
||||||
|
R1AkUTV74bU=";
|
||||||
|
};
|
||||||
12
akka-actor-tests/src/test/bind/etc/db.0
Executable file
12
akka-actor-tests/src/test/bind/etc/db.0
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
;
|
||||||
|
; BIND reverse data file for broadcast zone
|
||||||
|
;
|
||||||
|
$TTL 604800
|
||||||
|
@ IN SOA localhost. root.localhost. (
|
||||||
|
1 ; Serial
|
||||||
|
604800 ; Refresh
|
||||||
|
86400 ; Retry
|
||||||
|
2419200 ; Expire
|
||||||
|
604800 ) ; Negative Cache TTL
|
||||||
|
;
|
||||||
|
@ IN NS localhost.
|
||||||
13
akka-actor-tests/src/test/bind/etc/db.127
Executable file
13
akka-actor-tests/src/test/bind/etc/db.127
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
;
|
||||||
|
; BIND reverse data file for local loopback interface
|
||||||
|
;
|
||||||
|
$TTL 604800
|
||||||
|
@ IN SOA localhost. root.localhost. (
|
||||||
|
1 ; Serial
|
||||||
|
604800 ; Refresh
|
||||||
|
86400 ; Retry
|
||||||
|
2419200 ; Expire
|
||||||
|
604800 ) ; Negative Cache TTL
|
||||||
|
;
|
||||||
|
@ IN NS localhost.
|
||||||
|
1.0.0 IN PTR localhost.
|
||||||
12
akka-actor-tests/src/test/bind/etc/db.255
Executable file
12
akka-actor-tests/src/test/bind/etc/db.255
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
;
|
||||||
|
; BIND reverse data file for broadcast zone
|
||||||
|
;
|
||||||
|
$TTL 604800
|
||||||
|
@ IN SOA localhost. root.localhost. (
|
||||||
|
1 ; Serial
|
||||||
|
604800 ; Refresh
|
||||||
|
86400 ; Retry
|
||||||
|
2419200 ; Expire
|
||||||
|
604800 ) ; Negative Cache TTL
|
||||||
|
;
|
||||||
|
@ IN NS localhost.
|
||||||
13
akka-actor-tests/src/test/bind/etc/db.bar.example
Executable file
13
akka-actor-tests/src/test/bind/etc/db.bar.example
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
$TTL 86400
|
||||||
|
|
||||||
|
@ IN SOA bar.example root.bar.example (
|
||||||
|
2017010302
|
||||||
|
3600
|
||||||
|
900
|
||||||
|
604800
|
||||||
|
86400
|
||||||
|
)
|
||||||
|
|
||||||
|
@ IN NS example
|
||||||
|
example IN A 192.168.2.19
|
||||||
|
a-single IN A 192.168.2.20
|
||||||
14
akka-actor-tests/src/test/bind/etc/db.empty
Executable file
14
akka-actor-tests/src/test/bind/etc/db.empty
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
; BIND reverse data file for empty rfc1918 zone
|
||||||
|
;
|
||||||
|
; DO NOT EDIT THIS FILE - it is used for multiple zones.
|
||||||
|
; Instead, copy it, edit named.conf, and use that copy.
|
||||||
|
;
|
||||||
|
$TTL 86400
|
||||||
|
@ IN SOA localhost. root.localhost. (
|
||||||
|
1 ; Serial
|
||||||
|
604800 ; Refresh
|
||||||
|
86400 ; Retry
|
||||||
|
2419200 ; Expire
|
||||||
|
86400 ) ; Negative Cache TTL
|
||||||
|
;
|
||||||
|
@ IN NS localhost.
|
||||||
80
akka-actor-tests/src/test/bind/etc/db.foo.test
Executable file
80
akka-actor-tests/src/test/bind/etc/db.foo.test
Executable file
|
|
@ -0,0 +1,80 @@
|
||||||
|
$TTL 86400
|
||||||
|
|
||||||
|
@ IN SOA foo.test root.foo.test (
|
||||||
|
2017010302
|
||||||
|
3600
|
||||||
|
900
|
||||||
|
604800
|
||||||
|
86400
|
||||||
|
)
|
||||||
|
|
||||||
|
@ IN NS test
|
||||||
|
test IN A 192.168.1.19
|
||||||
|
a-single IN A 192.168.1.20
|
||||||
|
a-double IN A 192.168.1.21
|
||||||
|
a-double IN A 192.168.1.22
|
||||||
|
aaaa-single IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:1
|
||||||
|
aaaa-double IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:2
|
||||||
|
aaaa-double IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:3
|
||||||
|
a-aaaa IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:4
|
||||||
|
a-aaaa IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:5
|
||||||
|
a-aaaa IN A 192.168.1.23
|
||||||
|
a-aaaa IN A 192.168.1.24
|
||||||
|
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d00
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d01
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d02
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d03
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d04
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d05
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d06
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d07
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d08
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d09
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0a
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0b
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0c
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0d
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0e
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d0f
|
||||||
|
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d10
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d11
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d12
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d13
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d14
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d15
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d16
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d17
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d18
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d19
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1a
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1b
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1c
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1d
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1e
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d1f
|
||||||
|
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d20
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d21
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d22
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d23
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d24
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d25
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d26
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d27
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d28
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d29
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2a
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2b
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2c
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2d
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2e
|
||||||
|
many in AAAA 2001:985:965:1:ba27:ebff:fe5f:9d2f
|
||||||
|
|
||||||
|
service.tcp 86400 IN SRV 10 60 5060 a-single
|
||||||
|
service.tcp 86400 IN SRV 10 40 5070 a-double
|
||||||
|
|
||||||
|
cname-in IN CNAME a-double
|
||||||
|
cname-ext IN CNAME a-single.bar.example.
|
||||||
|
|
||||||
14
akka-actor-tests/src/test/bind/etc/db.local
Executable file
14
akka-actor-tests/src/test/bind/etc/db.local
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
;
|
||||||
|
; BIND data file for local loopback interface
|
||||||
|
;
|
||||||
|
$TTL 604800
|
||||||
|
@ IN SOA localhost. root.localhost. (
|
||||||
|
2 ; Serial
|
||||||
|
604800 ; Refresh
|
||||||
|
86400 ; Retry
|
||||||
|
2419200 ; Expire
|
||||||
|
604800 ) ; Negative Cache TTL
|
||||||
|
;
|
||||||
|
@ IN NS localhost.
|
||||||
|
@ IN A 127.0.0.1
|
||||||
|
@ IN AAAA ::1
|
||||||
90
akka-actor-tests/src/test/bind/etc/db.root
Executable file
90
akka-actor-tests/src/test/bind/etc/db.root
Executable file
|
|
@ -0,0 +1,90 @@
|
||||||
|
; This file holds the information on root name servers needed to
|
||||||
|
; initialize cache of Internet domain name servers
|
||||||
|
; (e.g. reference this file in the "cache . <file>"
|
||||||
|
; configuration file of BIND domain name servers).
|
||||||
|
;
|
||||||
|
; This file is made available by InterNIC
|
||||||
|
; under anonymous FTP as
|
||||||
|
; file /domain/named.cache
|
||||||
|
; on server FTP.INTERNIC.NET
|
||||||
|
; -OR- RS.INTERNIC.NET
|
||||||
|
;
|
||||||
|
; last update: February 17, 2016
|
||||||
|
; related version of root zone: 2016021701
|
||||||
|
;
|
||||||
|
; formerly NS.INTERNIC.NET
|
||||||
|
;
|
||||||
|
. 3600000 NS A.ROOT-SERVERS.NET.
|
||||||
|
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
|
||||||
|
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
|
||||||
|
;
|
||||||
|
; FORMERLY NS1.ISI.EDU
|
||||||
|
;
|
||||||
|
. 3600000 NS B.ROOT-SERVERS.NET.
|
||||||
|
B.ROOT-SERVERS.NET. 3600000 A 192.228.79.201
|
||||||
|
B.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:84::b
|
||||||
|
;
|
||||||
|
; FORMERLY C.PSI.NET
|
||||||
|
;
|
||||||
|
. 3600000 NS C.ROOT-SERVERS.NET.
|
||||||
|
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
|
||||||
|
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
|
||||||
|
;
|
||||||
|
; FORMERLY TERP.UMD.EDU
|
||||||
|
;
|
||||||
|
. 3600000 NS D.ROOT-SERVERS.NET.
|
||||||
|
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
|
||||||
|
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
|
||||||
|
;
|
||||||
|
; FORMERLY NS.NASA.GOV
|
||||||
|
;
|
||||||
|
. 3600000 NS E.ROOT-SERVERS.NET.
|
||||||
|
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
|
||||||
|
;
|
||||||
|
; FORMERLY NS.ISC.ORG
|
||||||
|
;
|
||||||
|
. 3600000 NS F.ROOT-SERVERS.NET.
|
||||||
|
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
|
||||||
|
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
|
||||||
|
;
|
||||||
|
; FORMERLY NS.NIC.DDN.MIL
|
||||||
|
;
|
||||||
|
. 3600000 NS G.ROOT-SERVERS.NET.
|
||||||
|
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
|
||||||
|
;
|
||||||
|
; FORMERLY AOS.ARL.ARMY.MIL
|
||||||
|
;
|
||||||
|
. 3600000 NS H.ROOT-SERVERS.NET.
|
||||||
|
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
|
||||||
|
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
|
||||||
|
;
|
||||||
|
; FORMERLY NIC.NORDU.NET
|
||||||
|
;
|
||||||
|
. 3600000 NS I.ROOT-SERVERS.NET.
|
||||||
|
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
|
||||||
|
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
|
||||||
|
;
|
||||||
|
; OPERATED BY VERISIGN, INC.
|
||||||
|
;
|
||||||
|
. 3600000 NS J.ROOT-SERVERS.NET.
|
||||||
|
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
|
||||||
|
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
|
||||||
|
;
|
||||||
|
; OPERATED BY RIPE NCC
|
||||||
|
;
|
||||||
|
. 3600000 NS K.ROOT-SERVERS.NET.
|
||||||
|
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
|
||||||
|
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
|
||||||
|
;
|
||||||
|
; OPERATED BY ICANN
|
||||||
|
;
|
||||||
|
. 3600000 NS L.ROOT-SERVERS.NET.
|
||||||
|
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
|
||||||
|
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:3::42
|
||||||
|
;
|
||||||
|
; OPERATED BY WIDE
|
||||||
|
;
|
||||||
|
. 3600000 NS M.ROOT-SERVERS.NET.
|
||||||
|
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
|
||||||
|
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
|
||||||
|
; End of file
|
||||||
11
akka-actor-tests/src/test/bind/etc/named.conf
Executable file
11
akka-actor-tests/src/test/bind/etc/named.conf
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// This is the primary configuration file for the BIND DNS server named.
|
||||||
|
//
|
||||||
|
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the
|
||||||
|
// structure of BIND configuration files in Debian, *BEFORE* you customize
|
||||||
|
// this configuration file.
|
||||||
|
//
|
||||||
|
// If you are just adding zones, please do that in /etc/bind/named.conf.local
|
||||||
|
|
||||||
|
include "/etc/bind/named.conf.options";
|
||||||
|
include "/etc/bind/named.conf.local";
|
||||||
|
include "/etc/bind/named.conf.default-zones";
|
||||||
30
akka-actor-tests/src/test/bind/etc/named.conf.default-zones
Executable file
30
akka-actor-tests/src/test/bind/etc/named.conf.default-zones
Executable file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// prime the server with knowledge of the root servers
|
||||||
|
zone "." {
|
||||||
|
type hint;
|
||||||
|
file "/etc/bind/db.root";
|
||||||
|
};
|
||||||
|
|
||||||
|
// be authoritative for the localhost forward and reverse zones, and for
|
||||||
|
// broadcast zones as per RFC 1912
|
||||||
|
|
||||||
|
zone "localhost" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.local";
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "127.in-addr.arpa" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.127";
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "0.in-addr.arpa" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "255.in-addr.arpa" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.255";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
17
akka-actor-tests/src/test/bind/etc/named.conf.local
Executable file
17
akka-actor-tests/src/test/bind/etc/named.conf.local
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// Do any local configuration here
|
||||||
|
//
|
||||||
|
|
||||||
|
// Consider adding the 1918 zones here, if they are not used in your
|
||||||
|
// organization
|
||||||
|
include "/etc/bind/zones.rfc1918";
|
||||||
|
|
||||||
|
zone "bar.example" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.bar.example";
|
||||||
|
};
|
||||||
|
|
||||||
|
zone "foo.test" {
|
||||||
|
type master;
|
||||||
|
file "/etc/bind/db.foo.test";
|
||||||
|
};
|
||||||
26
akka-actor-tests/src/test/bind/etc/named.conf.options
Executable file
26
akka-actor-tests/src/test/bind/etc/named.conf.options
Executable file
|
|
@ -0,0 +1,26 @@
|
||||||
|
options {
|
||||||
|
directory "/var/cache/bind";
|
||||||
|
|
||||||
|
// If there is a firewall between you and nameservers you want
|
||||||
|
// to talk to, you may need to fix the firewall to allow multiple
|
||||||
|
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
|
||||||
|
|
||||||
|
// If your ISP provided one or more IP addresses for stable
|
||||||
|
// nameservers, you probably want to use them as forwarders.
|
||||||
|
// Uncomment the following block, and insert the addresses replacing
|
||||||
|
// the all-0's placeholder.
|
||||||
|
|
||||||
|
// forwarders {
|
||||||
|
// 0.0.0.0;
|
||||||
|
// };
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// If BIND logs error messages about the root key being expired,
|
||||||
|
// you will need to update your keys. See https://www.isc.org/bind-keys
|
||||||
|
//========================================================================
|
||||||
|
dnssec-validation auto;
|
||||||
|
|
||||||
|
auth-nxdomain no; # conform to RFC1035
|
||||||
|
listen-on-v6 { any; };
|
||||||
|
};
|
||||||
|
|
||||||
4
akka-actor-tests/src/test/bind/etc/rndc.key
Executable file
4
akka-actor-tests/src/test/bind/etc/rndc.key
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
key "rndc-key" {
|
||||||
|
algorithm hmac-md5;
|
||||||
|
secret "WNiF81LrIxYbbPwt/twgUA==";
|
||||||
|
};
|
||||||
20
akka-actor-tests/src/test/bind/etc/zones.rfc1918
Executable file
20
akka-actor-tests/src/test/bind/etc/zones.rfc1918
Executable file
|
|
@ -0,0 +1,20 @@
|
||||||
|
zone "10.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
|
||||||
|
zone "16.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "17.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "18.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "19.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "20.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "21.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "22.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "23.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "24.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "25.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "26.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "27.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "28.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "29.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "30.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
zone "31.172.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
|
||||||
|
zone "168.192.in-addr.arpa" { type master; file "/etc/bind/db.empty"; };
|
||||||
|
|
@ -9,92 +9,36 @@ import java.net.InetAddress
|
||||||
import akka.io.dns.DnsProtocol.{ Ip, RequestType, Srv }
|
import akka.io.dns.DnsProtocol.{ Ip, RequestType, Srv }
|
||||||
import akka.io.{ Dns, IO }
|
import akka.io.{ Dns, IO }
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.{ AkkaSpec, SocketUtil }
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Relies on two zones setup, akka.test and akka.test2 e.g.
|
These tests rely on a DNS server with 2 zones configured, foo.test and bar.example.
|
||||||
* Install bind
|
|
||||||
* Create the two zones in /var/named/akka.test.zone and /var/named/akka.test2.zone
|
|
||||||
* Add the following to /etc/named.conf:
|
|
||||||
|
|
||||||
zone "akka.test" IN {
|
|
||||||
type master;
|
|
||||||
file "akka.test.zone";
|
|
||||||
};
|
|
||||||
|
|
||||||
zone "akka.test2" IN {
|
|
||||||
type master;
|
|
||||||
file "akka.test2.zone";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/var/named/akka.test.zone:
|
|
||||||
|
|
||||||
$TTL 86400
|
|
||||||
|
|
||||||
@ IN SOA akka.test root.akka.test (
|
|
||||||
2017010302
|
|
||||||
3600
|
|
||||||
900
|
|
||||||
604800
|
|
||||||
86400
|
|
||||||
)
|
|
||||||
|
|
||||||
@ IN NS test
|
|
||||||
test IN A 192.168.1.19
|
|
||||||
a-single IN A 192.168.1.20
|
|
||||||
a-double IN A 192.168.1.21
|
|
||||||
a-double IN A 192.168.1.22
|
|
||||||
aaaa-single IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:1
|
|
||||||
aaaa-double IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:2
|
|
||||||
aaaa-double IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:3
|
|
||||||
a-aaaa IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:4
|
|
||||||
a-aaaa IN AAAA fd4d:36b2:3eca:a2d8:0:0:0:5
|
|
||||||
a-aaaa IN A 192.168.1.23
|
|
||||||
a-aaaa IN A 192.168.1.24
|
|
||||||
|
|
||||||
service.tcp 86400 IN SRV 10 60 5060 a-single
|
|
||||||
service.tcp 86400 IN SRV 10 40 5070 a-double
|
|
||||||
|
|
||||||
cname-in IN CNAME a-double
|
|
||||||
cname-ext IN CNAME a-single.akka.test2.
|
|
||||||
|
|
||||||
/var/named/akka.test2.zone:
|
|
||||||
|
|
||||||
$TTL 86400
|
|
||||||
|
|
||||||
@ IN SOA akka.test2 root.akka.test2 (
|
|
||||||
2017010302
|
|
||||||
3600
|
|
||||||
900
|
|
||||||
604800
|
|
||||||
86400
|
|
||||||
)
|
|
||||||
|
|
||||||
@ IN NS test2
|
|
||||||
test2 IN A 192.168.2.19
|
|
||||||
a-single IN A 192.168.2.20
|
|
||||||
|
|
||||||
|
The configuration to start a bind DNS server in Docker with this configuration
|
||||||
|
is included, and the test will automatically start this container when the
|
||||||
|
test starts and tear it down when it finishes.
|
||||||
*/
|
*/
|
||||||
class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
"""
|
s"""
|
||||||
akka.loglevel = DEBUG
|
akka.loglevel = DEBUG
|
||||||
akka.io.dns.resolver = async-dns
|
akka.io.dns.resolver = async-dns
|
||||||
akka.io.dns.async-dns.nameservers = [localhost]
|
akka.io.dns.async-dns.nameservers = ["localhost:${AsyncDnsResolverIntegrationSpec.dockerDnsServerPort}"]
|
||||||
// akka.io.dns.async-dns.nameservers = default
|
// akka.io.dns.async-dns.nameservers = default
|
||||||
""") {
|
""") with DockerBindDnsService {
|
||||||
val duration = 10.seconds
|
val duration = 10.seconds
|
||||||
implicit val timeout = Timeout(duration)
|
implicit val timeout = Timeout(duration)
|
||||||
|
|
||||||
"Resolver" must {
|
val hostPort = AsyncDnsResolverIntegrationSpec.dockerDnsServerPort
|
||||||
|
|
||||||
pending // PENDING since needs `bind` server to be running to test end-to-end
|
"Resolver" must {
|
||||||
|
if (!dockerAvailable())
|
||||||
|
pending
|
||||||
|
|
||||||
"resolve single A record" in {
|
"resolve single A record" in {
|
||||||
val name = "a-single.akka.test"
|
val name = "a-single.foo.test"
|
||||||
val answer = resolve(name, DnsProtocol.Ip(ipv6 = false))
|
val answer = resolve(name, DnsProtocol.Ip(ipv6 = false))
|
||||||
withClue(answer) {
|
withClue(answer) {
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
|
|
@ -105,7 +49,7 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve double A records" in {
|
"resolve double A records" in {
|
||||||
val name = "a-double.akka.test"
|
val name = "a-double.foo.test"
|
||||||
val answer = resolve(name)
|
val answer = resolve(name)
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.map(_.asInstanceOf[ARecord].ip).toSet shouldEqual Set(
|
answer.records.map(_.asInstanceOf[ARecord].ip).toSet shouldEqual Set(
|
||||||
|
|
@ -115,14 +59,14 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve single AAAA record" in {
|
"resolve single AAAA record" in {
|
||||||
val name = "aaaa-single.akka.test"
|
val name = "aaaa-single.foo.test"
|
||||||
val answer = resolve(name)
|
val answer = resolve(name)
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.map(_.asInstanceOf[AAAARecord].ip) shouldEqual Seq(InetAddress.getByName("fd4d:36b2:3eca:a2d8:0:0:0:1"))
|
answer.records.map(_.asInstanceOf[AAAARecord].ip) shouldEqual Seq(InetAddress.getByName("fd4d:36b2:3eca:a2d8:0:0:0:1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve double AAAA records" in {
|
"resolve double AAAA records" in {
|
||||||
val name = "aaaa-double.akka.test"
|
val name = "aaaa-double.foo.test"
|
||||||
val answer = resolve(name)
|
val answer = resolve(name)
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.map(_.asInstanceOf[AAAARecord].ip).toSet shouldEqual Set(
|
answer.records.map(_.asInstanceOf[AAAARecord].ip).toSet shouldEqual Set(
|
||||||
|
|
@ -132,7 +76,7 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve mixed A/AAAA records" in {
|
"resolve mixed A/AAAA records" in {
|
||||||
val name = "a-aaaa.akka.test"
|
val name = "a-aaaa.foo.test"
|
||||||
val answer = resolve(name)
|
val answer = resolve(name)
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
|
|
||||||
|
|
@ -148,11 +92,11 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve external CNAME record" in {
|
"resolve external CNAME record" in {
|
||||||
val name = "cname-ext.akka.test"
|
val name = "cname-ext.foo.test"
|
||||||
val answer = (IO(Dns) ? DnsProtocol.Resolve(name)).mapTo[DnsProtocol.Resolved].futureValue
|
val answer = (IO(Dns) ? DnsProtocol.Resolve(name)).mapTo[DnsProtocol.Resolved].futureValue
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.collect { case r: CNameRecord ⇒ r.canonicalName }.toSet shouldEqual Set(
|
answer.records.collect { case r: CNameRecord ⇒ r.canonicalName }.toSet shouldEqual Set(
|
||||||
"a-single.akka.test2"
|
"a-single.bar.example"
|
||||||
)
|
)
|
||||||
answer.records.collect { case r: ARecord ⇒ r.ip }.toSet shouldEqual Set(
|
answer.records.collect { case r: ARecord ⇒ r.ip }.toSet shouldEqual Set(
|
||||||
InetAddress.getByName("192.168.2.20")
|
InetAddress.getByName("192.168.2.20")
|
||||||
|
|
@ -160,11 +104,11 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve internal CNAME record" in {
|
"resolve internal CNAME record" in {
|
||||||
val name = "cname-in.akka.test"
|
val name = "cname-in.foo.test"
|
||||||
val answer = resolve(name)
|
val answer = resolve(name)
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.collect { case r: CNameRecord ⇒ r.canonicalName }.toSet shouldEqual Set(
|
answer.records.collect { case r: CNameRecord ⇒ r.canonicalName }.toSet shouldEqual Set(
|
||||||
"a-double.akka.test"
|
"a-double.foo.test"
|
||||||
)
|
)
|
||||||
answer.records.collect { case r: ARecord ⇒ r.ip }.toSet shouldEqual Set(
|
answer.records.collect { case r: ARecord ⇒ r.ip }.toSet shouldEqual Set(
|
||||||
InetAddress.getByName("192.168.1.21"),
|
InetAddress.getByName("192.168.1.21"),
|
||||||
|
|
@ -173,29 +117,39 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec(
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve SRV record" in {
|
"resolve SRV record" in {
|
||||||
val name = "service.tcp.akka.test"
|
val name = "service.tcp.foo.test"
|
||||||
val answer = resolve("service.tcp.akka.test", Srv)
|
val answer = resolve("service.tcp.foo.test", Srv)
|
||||||
|
|
||||||
answer.name shouldEqual name
|
answer.name shouldEqual name
|
||||||
answer.records.collect { case r: SRVRecord ⇒ r }.toSet shouldEqual Set(
|
answer.records.collect { case r: SRVRecord ⇒ r }.toSet shouldEqual Set(
|
||||||
SRVRecord("service.tcp.akka.test", 86400, 10, 60, 5060, "a-single.akka.test"),
|
SRVRecord("service.tcp.foo.test", 86400, 10, 60, 5060, "a-single.foo.test"),
|
||||||
SRVRecord("service.tcp.akka.test", 86400, 10, 40, 5070, "a-double.akka.test")
|
SRVRecord("service.tcp.foo.test", 86400, 10, 40, 5070, "a-double.foo.test")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"resolve same address twice" in {
|
"resolve same address twice" in {
|
||||||
resolve("a-single.akka.test").records.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20"))
|
resolve("a-single.foo.test").records.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20"))
|
||||||
resolve("a-single.akka.test").records.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20"))
|
resolve("a-single.foo.test").records.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20"))
|
||||||
}
|
}
|
||||||
|
|
||||||
"handle nonexistent domains" in {
|
"handle nonexistent domains" in {
|
||||||
val answer = (IO(Dns) ? DnsProtocol.Resolve("nonexistent.akka.test")).mapTo[DnsProtocol.Resolved].futureValue
|
val answer = (IO(Dns) ? DnsProtocol.Resolve("nonexistent.foo.test")).mapTo[DnsProtocol.Resolved].futureValue
|
||||||
answer.records shouldEqual List.empty
|
answer.records shouldEqual List.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"resolve queries that are too big for UDP" in {
|
||||||
|
val name = "many.foo.test"
|
||||||
|
val answer = resolve(name)
|
||||||
|
answer.name shouldEqual name
|
||||||
|
answer.records.length should be(48)
|
||||||
|
}
|
||||||
|
|
||||||
def resolve(name: String, requestType: RequestType = Ip()): DnsProtocol.Resolved = {
|
def resolve(name: String, requestType: RequestType = Ip()): DnsProtocol.Resolved = {
|
||||||
(IO(Dns) ? DnsProtocol.Resolve(name, requestType)).mapTo[DnsProtocol.Resolved].futureValue
|
(IO(Dns) ? DnsProtocol.Resolve(name, requestType)).mapTo[DnsProtocol.Resolved].futureValue
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
object AsyncDnsResolverIntegrationSpec {
|
||||||
|
lazy val dockerDnsServerPort = SocketUtil.temporaryLocalPort()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.io.dns
|
||||||
|
|
||||||
|
import collection.JavaConverters._
|
||||||
|
import akka.testkit.AkkaSpec
|
||||||
|
import com.spotify.docker.client.DefaultDockerClient
|
||||||
|
import com.spotify.docker.client.DockerClient.LogsParam
|
||||||
|
import com.spotify.docker.client.messages.{ ContainerConfig, HostConfig, PortBinding }
|
||||||
|
import org.scalatest.concurrent.Eventually
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
|
trait DockerBindDnsService extends Eventually { self: AkkaSpec ⇒
|
||||||
|
val client = DefaultDockerClient.fromEnv().build()
|
||||||
|
|
||||||
|
val hostPort: Int
|
||||||
|
|
||||||
|
var id: Option[String] = None
|
||||||
|
|
||||||
|
def dockerAvailable() = Try(client.ping()).isSuccess
|
||||||
|
|
||||||
|
override def atStartup(): Unit = {
|
||||||
|
self.atStartup()
|
||||||
|
|
||||||
|
// https://github.com/sameersbn/docker-bind/pull/61
|
||||||
|
val image = "raboof/bind:9.11.3-20180713-nochown"
|
||||||
|
try {
|
||||||
|
client.pull(image)
|
||||||
|
} catch {
|
||||||
|
case NonFatal(_) ⇒
|
||||||
|
log.warning(s"Failed to pull docker image [$image], is docker running?")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val containerConfig = ContainerConfig.builder()
|
||||||
|
.image(image)
|
||||||
|
.env("NO_CHOWN=true")
|
||||||
|
.hostConfig(
|
||||||
|
HostConfig.builder()
|
||||||
|
.portBindings(Map(
|
||||||
|
"53/tcp" -> List(PortBinding.of("", hostPort)).asJava,
|
||||||
|
"53/udp" -> List(PortBinding.of("", hostPort)).asJava
|
||||||
|
).asJava)
|
||||||
|
.binds(HostConfig.Bind.from(new java.io.File("akka-actor-tests/src/test/bind/").getAbsolutePath).to("/data/bind").build())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val creation = client.createContainer(containerConfig, "akka-test-dns-" + getClass.getCanonicalName)
|
||||||
|
creation.warnings() should be(null)
|
||||||
|
id = Some(creation.id())
|
||||||
|
|
||||||
|
client.startContainer(creation.id())
|
||||||
|
|
||||||
|
eventually {
|
||||||
|
client.logs(creation.id(), LogsParam.stderr()).readFully() should include("all zones loaded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def afterTermination(): Unit = {
|
||||||
|
self.afterTermination()
|
||||||
|
id.foreach(client.killContainer)
|
||||||
|
id.foreach(client.removeContainer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.io.dns.internal
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
import scala.collection.immutable.Seq
|
||||||
|
import akka.actor.Props
|
||||||
|
import akka.io.Udp
|
||||||
|
import akka.io.dns.{ RecordClass, RecordType }
|
||||||
|
import akka.io.dns.internal.DnsClient.{ Answer, Question4 }
|
||||||
|
import akka.testkit.{ AkkaSpec, ImplicitSender, TestProbe }
|
||||||
|
|
||||||
|
class DnsClientSpec extends AkkaSpec with ImplicitSender {
|
||||||
|
"The async DNS client" should {
|
||||||
|
val exampleRequest = Question4(42, "akka.io")
|
||||||
|
val exampleRequestMessage =
|
||||||
|
Message(42, MessageFlags(), questions = Seq(Question("akka.io", RecordType.A, RecordClass.IN)))
|
||||||
|
val exampleResponseMessage = Message(42, MessageFlags(answer = true))
|
||||||
|
val exampleResponse = Answer(42, Nil)
|
||||||
|
val dnsServerAddress = InetSocketAddress.createUnresolved("foo", 53)
|
||||||
|
val localAddress = InetSocketAddress.createUnresolved("localhost", 13441)
|
||||||
|
|
||||||
|
"not connect to the DNS server over TCP eagerly" in {
|
||||||
|
val udpExtensionProbe = TestProbe()
|
||||||
|
val tcpClientCreated = new AtomicBoolean(false)
|
||||||
|
|
||||||
|
val client = system.actorOf(Props(new DnsClient(dnsServerAddress) {
|
||||||
|
override val udp = udpExtensionProbe.ref
|
||||||
|
|
||||||
|
override def createTcpClient = {
|
||||||
|
tcpClientCreated.set(true)
|
||||||
|
TestProbe().ref
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
client ! exampleRequest
|
||||||
|
|
||||||
|
udpExtensionProbe.expectMsgType[Udp.Bind]
|
||||||
|
udpExtensionProbe.lastSender ! Udp.Bound(InetSocketAddress.createUnresolved("localhost", 41325))
|
||||||
|
|
||||||
|
expectMsgType[Udp.Send]
|
||||||
|
client ! Udp.Received(exampleResponseMessage.write(), dnsServerAddress)
|
||||||
|
|
||||||
|
expectMsg(exampleResponse)
|
||||||
|
|
||||||
|
tcpClientCreated.get() should be(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
"Fall back to TCP when the UDP response is truncated" in {
|
||||||
|
val udpExtensionProbe = TestProbe()
|
||||||
|
val tcpClientProbe = TestProbe()
|
||||||
|
|
||||||
|
val client = system.actorOf(Props(new DnsClient(dnsServerAddress) {
|
||||||
|
override val udp = udpExtensionProbe.ref
|
||||||
|
|
||||||
|
override def createTcpClient = tcpClientProbe.ref
|
||||||
|
}))
|
||||||
|
|
||||||
|
client ! exampleRequest
|
||||||
|
|
||||||
|
udpExtensionProbe.expectMsgType[Udp.Bind]
|
||||||
|
udpExtensionProbe.lastSender ! Udp.Bound(InetSocketAddress.createUnresolved("localhost", 41325))
|
||||||
|
|
||||||
|
expectMsgType[Udp.Send]
|
||||||
|
client ! Udp.Received(Message(exampleRequest.id, MessageFlags(truncated = true)).write(), dnsServerAddress)
|
||||||
|
|
||||||
|
tcpClientProbe.expectMsg(exampleRequestMessage)
|
||||||
|
tcpClientProbe.reply(exampleResponse)
|
||||||
|
|
||||||
|
expectMsg(exampleResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.io.dns.internal
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
|
||||||
|
import akka.actor.{ Props, Terminated }
|
||||||
|
import akka.io.Tcp
|
||||||
|
import akka.io.Tcp.{ CommandFailed, Connected, PeerClosed, Register }
|
||||||
|
import akka.io.dns.{ RecordClass, RecordType }
|
||||||
|
import akka.io.dns.internal.DnsClient.Answer
|
||||||
|
import akka.testkit.{ AkkaSpec, ImplicitSender, TestProbe }
|
||||||
|
|
||||||
|
import scala.collection.immutable.Seq
|
||||||
|
|
||||||
|
class TcpDnsClientSpec extends AkkaSpec with ImplicitSender {
|
||||||
|
import TcpDnsClient._
|
||||||
|
|
||||||
|
"The async TCP DNS client" should {
|
||||||
|
val exampleRequestMessage =
|
||||||
|
Message(42, MessageFlags(), questions = Seq(Question("akka.io", RecordType.A, RecordClass.IN)))
|
||||||
|
val exampleResponseMessage = Message(42, MessageFlags(answer = true))
|
||||||
|
val dnsServerAddress = InetSocketAddress.createUnresolved("foo", 53)
|
||||||
|
val localAddress = InetSocketAddress.createUnresolved("localhost", 13441)
|
||||||
|
|
||||||
|
"reconnect when the server closes the connection" in {
|
||||||
|
val tcpExtensionProbe = TestProbe()
|
||||||
|
val answerProbe = TestProbe()
|
||||||
|
|
||||||
|
val client = system.actorOf(Props(new TcpDnsClient(tcpExtensionProbe.ref, dnsServerAddress, answerProbe.ref)))
|
||||||
|
|
||||||
|
client ! exampleRequestMessage
|
||||||
|
|
||||||
|
tcpExtensionProbe.expectMsg(Tcp.Connect(dnsServerAddress))
|
||||||
|
tcpExtensionProbe.lastSender ! Connected(dnsServerAddress, localAddress)
|
||||||
|
expectMsgType[Register]
|
||||||
|
val registered = tcpExtensionProbe.lastSender
|
||||||
|
|
||||||
|
expectMsgType[Tcp.Write]
|
||||||
|
registered ! Tcp.Received(encodeLength(exampleResponseMessage.write().length) ++ exampleResponseMessage.write())
|
||||||
|
|
||||||
|
answerProbe.expectMsg(Answer(42, Nil))
|
||||||
|
|
||||||
|
// When a new request arrived after the connection is closed
|
||||||
|
registered ! PeerClosed
|
||||||
|
client ! exampleRequestMessage
|
||||||
|
|
||||||
|
// Expect a reconnect
|
||||||
|
tcpExtensionProbe.expectMsg(Tcp.Connect(dnsServerAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept a fragmented TCP response" in {
|
||||||
|
val tcpExtensionProbe = TestProbe()
|
||||||
|
val answerProbe = TestProbe()
|
||||||
|
|
||||||
|
val client = system.actorOf(Props(new TcpDnsClient(tcpExtensionProbe.ref, dnsServerAddress, answerProbe.ref)))
|
||||||
|
|
||||||
|
client ! exampleRequestMessage
|
||||||
|
|
||||||
|
tcpExtensionProbe.expectMsg(Tcp.Connect(dnsServerAddress))
|
||||||
|
tcpExtensionProbe.lastSender ! Connected(dnsServerAddress, localAddress)
|
||||||
|
expectMsgType[Register]
|
||||||
|
val registered = tcpExtensionProbe.lastSender
|
||||||
|
|
||||||
|
expectMsgType[Tcp.Write]
|
||||||
|
val fullResponse = encodeLength(exampleResponseMessage.write().length) ++ exampleResponseMessage.write()
|
||||||
|
registered ! Tcp.Received(fullResponse.take(8))
|
||||||
|
registered ! Tcp.Received(fullResponse.drop(8))
|
||||||
|
|
||||||
|
answerProbe.expectMsg(Answer(42, Nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept merged TCP responses" in {
|
||||||
|
val tcpExtensionProbe = TestProbe()
|
||||||
|
val answerProbe = TestProbe()
|
||||||
|
|
||||||
|
val client = system.actorOf(Props(new TcpDnsClient(tcpExtensionProbe.ref, dnsServerAddress, answerProbe.ref)))
|
||||||
|
|
||||||
|
client ! exampleRequestMessage
|
||||||
|
client ! exampleRequestMessage.copy(id = 43)
|
||||||
|
|
||||||
|
tcpExtensionProbe.expectMsg(Tcp.Connect(dnsServerAddress))
|
||||||
|
tcpExtensionProbe.lastSender ! Connected(dnsServerAddress, localAddress)
|
||||||
|
expectMsgType[Register]
|
||||||
|
val registered = tcpExtensionProbe.lastSender
|
||||||
|
|
||||||
|
expectMsgType[Tcp.Write]
|
||||||
|
expectMsgType[Tcp.Write]
|
||||||
|
val fullResponse =
|
||||||
|
encodeLength(exampleResponseMessage.write().length) ++ exampleResponseMessage.write() ++
|
||||||
|
encodeLength(exampleResponseMessage.write().length) ++ exampleResponseMessage.copy(id = 43).write()
|
||||||
|
registered ! Tcp.Received(fullResponse.take(8))
|
||||||
|
registered ! Tcp.Received(fullResponse.drop(8))
|
||||||
|
|
||||||
|
answerProbe.expectMsg(Answer(42, Nil))
|
||||||
|
answerProbe.expectMsg(Answer(43, Nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,13 +7,15 @@ package akka.io.dns.internal
|
||||||
import java.net.{ InetAddress, InetSocketAddress }
|
import java.net.{ InetAddress, InetSocketAddress }
|
||||||
|
|
||||||
import akka.actor.Status.Failure
|
import akka.actor.Status.Failure
|
||||||
import akka.actor.{ Actor, ActorLogging, ActorRef, NoSerializationVerificationNeeded, Stash }
|
import akka.actor.{ Actor, ActorLogging, ActorRef, NoSerializationVerificationNeeded, Props, Stash }
|
||||||
import akka.annotation.InternalApi
|
import akka.annotation.InternalApi
|
||||||
import akka.io.dns.{ RecordClass, RecordType, ResourceRecord }
|
import akka.io.dns.{ RecordClass, RecordType, ResourceRecord }
|
||||||
import akka.io.{ IO, Udp }
|
import akka.io.{ IO, Tcp, Udp }
|
||||||
|
import akka.pattern.BackoffSupervisor
|
||||||
|
|
||||||
import scala.collection.{ immutable ⇒ im }
|
import scala.collection.{ immutable ⇒ im }
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
|
|
@ -38,13 +40,20 @@ import scala.util.Try
|
||||||
|
|
||||||
import context.system
|
import context.system
|
||||||
|
|
||||||
IO(Udp) ! Udp.Bind(self, new InetSocketAddress(InetAddress.getByAddress(Array.ofDim(4)), 0))
|
val udp = IO(Udp)
|
||||||
|
val tcp = IO(Tcp)
|
||||||
|
|
||||||
var inflightRequests: Map[Short, ActorRef] = Map.empty
|
var inflightRequests: Map[Short, (ActorRef, Message)] = Map.empty
|
||||||
|
|
||||||
|
lazy val tcpDnsClient: ActorRef = createTcpClient()
|
||||||
|
|
||||||
|
override def preStart() = {
|
||||||
|
udp ! Udp.Bind(self, new InetSocketAddress(InetAddress.getByAddress(Array.ofDim(4)), 0))
|
||||||
|
}
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case Udp.Bound(local) ⇒
|
case Udp.Bound(local) ⇒
|
||||||
log.debug(s"Bound to UDP address [{}]", local)
|
log.debug("Bound to UDP address [{}]", local)
|
||||||
context.become(ready(sender()))
|
context.become(ready(sender()))
|
||||||
unstashAll()
|
unstashAll()
|
||||||
case _: Question4 ⇒
|
case _: Question4 ⇒
|
||||||
|
|
@ -65,23 +74,23 @@ import scala.util.Try
|
||||||
inflightRequests -= id
|
inflightRequests -= id
|
||||||
case Question4(id, name) ⇒
|
case Question4(id, name) ⇒
|
||||||
log.debug("Resolving [{}] (A)", name)
|
log.debug("Resolving [{}] (A)", name)
|
||||||
inflightRequests += (id -> sender())
|
|
||||||
val msg = message(name, id, RecordType.A)
|
val msg = message(name, id, RecordType.A)
|
||||||
log.debug(s"Message [{}] to [{}]: [{}]", id, ns, msg)
|
inflightRequests += (id -> (sender(), msg))
|
||||||
|
log.debug("Message [{}] to [{}]: [{}]", id, ns, msg)
|
||||||
socket ! Udp.Send(msg.write(), ns)
|
socket ! Udp.Send(msg.write(), ns)
|
||||||
|
|
||||||
case Question6(id, name) ⇒
|
case Question6(id, name) ⇒
|
||||||
log.debug("Resolving [{}] (AAAA)", name)
|
log.debug("Resolving [{}] (AAAA)", name)
|
||||||
inflightRequests += (id -> sender())
|
|
||||||
val msg = message(name, id, RecordType.AAAA)
|
val msg = message(name, id, RecordType.AAAA)
|
||||||
log.debug(s"Message to [{}]: [{}]", ns, msg)
|
inflightRequests += (id -> (sender(), msg))
|
||||||
|
log.debug("Message to [{}]: [{}]", ns, msg)
|
||||||
socket ! Udp.Send(msg.write(), ns)
|
socket ! Udp.Send(msg.write(), ns)
|
||||||
|
|
||||||
case SrvQuestion(id, name) ⇒
|
case SrvQuestion(id, name) ⇒
|
||||||
log.debug("Resolving [{}] (SRV)", name)
|
log.debug("Resolving [{}] (SRV)", name)
|
||||||
inflightRequests += (id -> sender())
|
|
||||||
val msg = message(name, id, RecordType.SRV)
|
val msg = message(name, id, RecordType.SRV)
|
||||||
log.debug(s"Message to {}: msg", ns, msg)
|
inflightRequests += (id -> (sender(), msg))
|
||||||
|
log.debug("Message to [{}]: [{}]", ns, msg)
|
||||||
socket ! Udp.Send(msg.write(), ns)
|
socket ! Udp.Send(msg.write(), ns)
|
||||||
|
|
||||||
case Udp.CommandFailed(cmd) ⇒
|
case Udp.CommandFailed(cmd) ⇒
|
||||||
|
|
@ -91,33 +100,51 @@ import scala.util.Try
|
||||||
// best effort, don't throw
|
// best effort, don't throw
|
||||||
Try {
|
Try {
|
||||||
val msg = Message.parse(send.payload)
|
val msg = Message.parse(send.payload)
|
||||||
inflightRequests.get(msg.id).foreach { s ⇒
|
inflightRequests.get(msg.id).foreach {
|
||||||
s ! Failure(new RuntimeException("Send failed to nameserver"))
|
case (s, _) ⇒
|
||||||
inflightRequests -= msg.id
|
s ! Failure(new RuntimeException("Send failed to nameserver"))
|
||||||
|
inflightRequests -= msg.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
log.warning("Dns client failed to send {}", cmd)
|
log.warning("Dns client failed to send {}", cmd)
|
||||||
}
|
}
|
||||||
case Udp.Received(data, remote) ⇒
|
case Udp.Received(data, remote) ⇒
|
||||||
log.debug(s"Received message from [{}]: [{}]", remote, data)
|
log.debug("Received message from [{}]: [{}]", remote, data)
|
||||||
val msg = Message.parse(data)
|
val msg = Message.parse(data)
|
||||||
log.debug(s"Decoded: $msg")
|
log.debug("Decoded UDP DNS response [{}]", data)
|
||||||
// TODO remove me when #25460 is implemented
|
|
||||||
if (msg.flags.isTruncated) {
|
if (msg.flags.isTruncated) {
|
||||||
log.warning("DNS response truncated and fallback to TCP is not yet implemented. See #25460")
|
log.debug("DNS response truncated, falling back to TCP")
|
||||||
|
inflightRequests.get(msg.id) match {
|
||||||
|
case Some((_, msg)) ⇒
|
||||||
|
tcpDnsClient ! msg
|
||||||
|
case _ ⇒
|
||||||
|
log.debug("Client for id {} not found. Discarding unsuccessful response.", msg.id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val (recs, additionalRecs) = if (msg.flags.responseCode == ResponseCode.SUCCESS) (msg.answerRecs, msg.additionalRecs) else (Nil, Nil)
|
||||||
|
self ! Answer(msg.id, recs, additionalRecs)
|
||||||
}
|
}
|
||||||
val (recs, additionalRecs) = if (msg.flags.responseCode == ResponseCode.SUCCESS) (msg.answerRecs, msg.additionalRecs) else (Nil, Nil)
|
case response: Answer ⇒
|
||||||
val response = Answer(msg.id, recs, additionalRecs)
|
|
||||||
inflightRequests.get(response.id) match {
|
inflightRequests.get(response.id) match {
|
||||||
case Some(reply) ⇒
|
case Some((reply, _)) ⇒
|
||||||
reply ! response
|
reply ! response
|
||||||
inflightRequests -= response.id
|
inflightRequests -= response.id
|
||||||
case None ⇒
|
case None ⇒
|
||||||
log.debug("Client for id {} not found. Discarding response.", response.id)
|
log.debug("Client for id {} not found. Discarding response.", response.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Udp.Unbind ⇒ socket ! Udp.Unbind
|
case Udp.Unbind ⇒ socket ! Udp.Unbind
|
||||||
case Udp.Unbound ⇒ context.stop(self)
|
case Udp.Unbound ⇒ context.stop(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def createTcpClient() = {
|
||||||
|
context.actorOf(BackoffSupervisor.props(
|
||||||
|
Props(classOf[TcpDnsClient], tcp, ns, self),
|
||||||
|
childName = "tcpDnsClient",
|
||||||
|
minBackoff = 10.millis,
|
||||||
|
maxBackoff = 20.seconds,
|
||||||
|
randomFactor = 0.1
|
||||||
|
), "tcpDnsClientSupervisor")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.io.dns.internal
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
|
||||||
|
import akka.AkkaException
|
||||||
|
import akka.actor.{ Actor, ActorLogging, ActorRef, Stash }
|
||||||
|
import akka.annotation.InternalApi
|
||||||
|
import akka.io.Tcp._
|
||||||
|
import akka.io.dns.internal.DnsClient.{ Answer, DnsQuestion, Question4 }
|
||||||
|
import akka.io.{ IO, Tcp }
|
||||||
|
import akka.util.ByteString
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL API
|
||||||
|
*/
|
||||||
|
@InternalApi private[akka] class TcpDnsClient(tcp: ActorRef, ns: InetSocketAddress, answerRecipient: ActorRef) extends Actor with ActorLogging with Stash {
|
||||||
|
import TcpDnsClient._
|
||||||
|
|
||||||
|
import context.system
|
||||||
|
|
||||||
|
override def receive: Receive = idle
|
||||||
|
|
||||||
|
val idle: Receive = {
|
||||||
|
case _: Message ⇒
|
||||||
|
stash()
|
||||||
|
log.debug("Connecting to [{}]", ns)
|
||||||
|
tcp ! Tcp.Connect(ns)
|
||||||
|
context.become(connecting)
|
||||||
|
}
|
||||||
|
|
||||||
|
val connecting: Receive = {
|
||||||
|
case failure @ CommandFailed(_: Connect) ⇒
|
||||||
|
throwFailure(s"Failed to connect to TCP DNS server at [$ns]", failure.cause)
|
||||||
|
case _: Tcp.Connected ⇒
|
||||||
|
log.debug("Connected to TCP address [{}]", ns)
|
||||||
|
val connection = sender()
|
||||||
|
context.become(ready(connection))
|
||||||
|
connection ! Register(self)
|
||||||
|
unstashAll()
|
||||||
|
case _: Message ⇒
|
||||||
|
stash()
|
||||||
|
}
|
||||||
|
|
||||||
|
def ready(connection: ActorRef, buffer: ByteString = ByteString.empty): Receive = {
|
||||||
|
case msg: Message ⇒
|
||||||
|
val bytes = msg.write()
|
||||||
|
connection ! Tcp.Write(encodeLength(bytes.length) ++ bytes)
|
||||||
|
case failure @ CommandFailed(_: Write) ⇒
|
||||||
|
throwFailure("Write failed", failure.cause)
|
||||||
|
case Received(newData) ⇒
|
||||||
|
val data = buffer ++ newData
|
||||||
|
// TCP DNS responses are prefixed by 2 bytes encoding the length of the response
|
||||||
|
val prefixSize = 2
|
||||||
|
if (data.length < prefixSize)
|
||||||
|
context.become(ready(connection, data))
|
||||||
|
else {
|
||||||
|
val expectedPayloadLength = decodeLength(data)
|
||||||
|
if (data.drop(prefixSize).length < expectedPayloadLength)
|
||||||
|
context.become(ready(connection, data))
|
||||||
|
else {
|
||||||
|
answerRecipient ! parseResponse(data.drop(prefixSize))
|
||||||
|
context.become(ready(connection))
|
||||||
|
if (data.length > prefixSize + expectedPayloadLength) {
|
||||||
|
self ! Received(data.drop(prefixSize + expectedPayloadLength))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PeerClosed ⇒
|
||||||
|
context.become(idle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def parseResponse(data: ByteString) = {
|
||||||
|
val msg = Message.parse(data)
|
||||||
|
log.debug("Decoded TCP DNS response [{}]", msg)
|
||||||
|
if (msg.flags.isTruncated) {
|
||||||
|
log.warning("TCP DNS response truncated")
|
||||||
|
}
|
||||||
|
val (recs, additionalRecs) = if (msg.flags.responseCode == ResponseCode.SUCCESS) (msg.answerRecs, msg.additionalRecs) else (Nil, Nil)
|
||||||
|
Answer(msg.id, recs, additionalRecs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private[internal] object TcpDnsClient {
|
||||||
|
def encodeLength(length: Int): ByteString =
|
||||||
|
ByteString((length / 256).toByte, length.toByte)
|
||||||
|
|
||||||
|
def decodeLength(data: ByteString): Int =
|
||||||
|
((data(0).toInt + 256) % 256) * 256 + ((data(1) + 256) % 256)
|
||||||
|
|
||||||
|
def throwFailure(message: String, cause: Option[Throwable]): Unit =
|
||||||
|
cause match {
|
||||||
|
case None ⇒
|
||||||
|
throw new AkkaException(message)
|
||||||
|
case Some(throwable) ⇒
|
||||||
|
throw new AkkaException(message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -101,6 +101,9 @@ object Dependencies {
|
||||||
// in-memory filesystem for file related tests
|
// in-memory filesystem for file related tests
|
||||||
val jimfs = "com.google.jimfs" % "jimfs" % "1.1" % "test" // ApacheV2
|
val jimfs = "com.google.jimfs" % "jimfs" % "1.1" % "test" // ApacheV2
|
||||||
|
|
||||||
|
// docker utils
|
||||||
|
val dockerClient = "com.spotify" % "docker-client" % "8.13.1" % "test" // ApacheV2
|
||||||
|
|
||||||
// metrics, measurements, perf testing
|
// metrics, measurements, perf testing
|
||||||
val metrics = "io.dropwizard.metrics" % "metrics-core" % "3.2.5" % "test" // ApacheV2
|
val metrics = "io.dropwizard.metrics" % "metrics-core" % "3.2.5" % "test" // ApacheV2
|
||||||
val metricsJvm = "io.dropwizard.metrics" % "metrics-jvm" % "3.2.5" % "test" // ApacheV2
|
val metricsJvm = "io.dropwizard.metrics" % "metrics-jvm" % "3.2.5" % "test" // ApacheV2
|
||||||
|
|
@ -147,8 +150,11 @@ object Dependencies {
|
||||||
|
|
||||||
val testkit = l ++= Seq(Test.junit, Test.scalatest.value) ++ Test.metricsAll
|
val testkit = l ++= Seq(Test.junit, Test.scalatest.value) ++ Test.metricsAll
|
||||||
|
|
||||||
val actorTests = l ++= Seq(Test.junit, Test.scalatest.value, Test.commonsCodec, Test.commonsMath,
|
val actorTests = l ++= Seq(
|
||||||
Test.mockito, Test.scalacheck.value, Test.jimfs)
|
Test.junit, Test.scalatest.value, Test.commonsCodec, Test.commonsMath,
|
||||||
|
Test.mockito, Test.scalacheck.value, Test.jimfs,
|
||||||
|
Test.dockerClient
|
||||||
|
)
|
||||||
|
|
||||||
val actorTestkitTyped = l ++= Seq(Provided.junit, Provided.scalatest.value)
|
val actorTestkitTyped = l ++= Seq(Provided.junit, Provided.scalatest.value)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue