diff --git a/akka-actor-tests/src/test/bind/etc/bind.keys b/akka-actor-tests/src/test/bind/etc/bind.keys new file mode 100755 index 00000000000..5e5a32ba9c7 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/bind.keys @@ -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="; +}; diff --git a/akka-actor-tests/src/test/bind/etc/db.0 b/akka-actor-tests/src/test/bind/etc/db.0 new file mode 100755 index 00000000000..e3aabdbeed1 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.0 @@ -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. diff --git a/akka-actor-tests/src/test/bind/etc/db.127 b/akka-actor-tests/src/test/bind/etc/db.127 new file mode 100755 index 00000000000..cd05bef14a5 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.127 @@ -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. diff --git a/akka-actor-tests/src/test/bind/etc/db.255 b/akka-actor-tests/src/test/bind/etc/db.255 new file mode 100755 index 00000000000..e3aabdbeed1 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.255 @@ -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. diff --git a/akka-actor-tests/src/test/bind/etc/db.bar.example b/akka-actor-tests/src/test/bind/etc/db.bar.example new file mode 100755 index 00000000000..fdbde5dd334 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.bar.example @@ -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 diff --git a/akka-actor-tests/src/test/bind/etc/db.empty b/akka-actor-tests/src/test/bind/etc/db.empty new file mode 100755 index 00000000000..8a128589a18 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.empty @@ -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. diff --git a/akka-actor-tests/src/test/bind/etc/db.foo.test b/akka-actor-tests/src/test/bind/etc/db.foo.test new file mode 100755 index 00000000000..4386b61b663 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.foo.test @@ -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. + diff --git a/akka-actor-tests/src/test/bind/etc/db.local b/akka-actor-tests/src/test/bind/etc/db.local new file mode 100755 index 00000000000..2f272d40838 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.local @@ -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 diff --git a/akka-actor-tests/src/test/bind/etc/db.root b/akka-actor-tests/src/test/bind/etc/db.root new file mode 100755 index 00000000000..f0b79d2af39 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/db.root @@ -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 . " +; 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 diff --git a/akka-actor-tests/src/test/bind/etc/named.conf b/akka-actor-tests/src/test/bind/etc/named.conf new file mode 100755 index 00000000000..880786afdc8 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/named.conf @@ -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"; diff --git a/akka-actor-tests/src/test/bind/etc/named.conf.default-zones b/akka-actor-tests/src/test/bind/etc/named.conf.default-zones new file mode 100755 index 00000000000..355338bdaa5 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/named.conf.default-zones @@ -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"; +}; + + diff --git a/akka-actor-tests/src/test/bind/etc/named.conf.local b/akka-actor-tests/src/test/bind/etc/named.conf.local new file mode 100755 index 00000000000..55e6e4ae322 --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/named.conf.local @@ -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"; +}; diff --git a/akka-actor-tests/src/test/bind/etc/named.conf.options b/akka-actor-tests/src/test/bind/etc/named.conf.options new file mode 100755 index 00000000000..b1bef512afd --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/named.conf.options @@ -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; }; +}; + diff --git a/akka-actor-tests/src/test/bind/etc/rndc.key b/akka-actor-tests/src/test/bind/etc/rndc.key new file mode 100755 index 00000000000..b3be32ede3a --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/rndc.key @@ -0,0 +1,4 @@ +key "rndc-key" { + algorithm hmac-md5; + secret "WNiF81LrIxYbbPwt/twgUA=="; +}; diff --git a/akka-actor-tests/src/test/bind/etc/zones.rfc1918 b/akka-actor-tests/src/test/bind/etc/zones.rfc1918 new file mode 100755 index 00000000000..03b5546296d --- /dev/null +++ b/akka-actor-tests/src/test/bind/etc/zones.rfc1918 @@ -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"; }; diff --git a/akka-actor-tests/src/test/scala/akka/io/dns/AsyncDnsResolverIntegrationSpec.scala b/akka-actor-tests/src/test/scala/akka/io/dns/AsyncDnsResolverIntegrationSpec.scala index bad7b6cad45..ac7904ccffe 100644 --- a/akka-actor-tests/src/test/scala/akka/io/dns/AsyncDnsResolverIntegrationSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/io/dns/AsyncDnsResolverIntegrationSpec.scala @@ -9,92 +9,36 @@ import java.net.InetAddress import akka.io.dns.DnsProtocol.{ Ip, RequestType, Srv } import akka.io.{ Dns, IO } import akka.pattern.ask -import akka.testkit.AkkaSpec +import akka.testkit.{ AkkaSpec, SocketUtil } import akka.util.Timeout import scala.concurrent.duration._ /* -Relies on two zones setup, akka.test and akka.test2 e.g. -* 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 +These tests rely on a DNS server with 2 zones configured, foo.test and bar.example. +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( - """ + s""" akka.loglevel = DEBUG 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 - """) { + """) with DockerBindDnsService { val duration = 10.seconds 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 { - val name = "a-single.akka.test" + val name = "a-single.foo.test" val answer = resolve(name, DnsProtocol.Ip(ipv6 = false)) withClue(answer) { answer.name shouldEqual name @@ -105,7 +49,7 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "resolve double A records" in { - val name = "a-double.akka.test" + val name = "a-double.foo.test" val answer = resolve(name) answer.name shouldEqual name answer.records.map(_.asInstanceOf[ARecord].ip).toSet shouldEqual Set( @@ -115,14 +59,14 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "resolve single AAAA record" in { - val name = "aaaa-single.akka.test" + val name = "aaaa-single.foo.test" val answer = resolve(name) answer.name shouldEqual name answer.records.map(_.asInstanceOf[AAAARecord].ip) shouldEqual Seq(InetAddress.getByName("fd4d:36b2:3eca:a2d8:0:0:0:1")) } "resolve double AAAA records" in { - val name = "aaaa-double.akka.test" + val name = "aaaa-double.foo.test" val answer = resolve(name) answer.name shouldEqual name answer.records.map(_.asInstanceOf[AAAARecord].ip).toSet shouldEqual Set( @@ -132,7 +76,7 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "resolve mixed A/AAAA records" in { - val name = "a-aaaa.akka.test" + val name = "a-aaaa.foo.test" val answer = resolve(name) answer.name shouldEqual name @@ -148,11 +92,11 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "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 answer.name shouldEqual name 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( InetAddress.getByName("192.168.2.20") @@ -160,11 +104,11 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "resolve internal CNAME record" in { - val name = "cname-in.akka.test" + val name = "cname-in.foo.test" val answer = resolve(name) answer.name shouldEqual name 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( InetAddress.getByName("192.168.1.21"), @@ -173,29 +117,39 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( } "resolve SRV record" in { - val name = "service.tcp.akka.test" - val answer = resolve("service.tcp.akka.test", Srv) + val name = "service.tcp.foo.test" + val answer = resolve("service.tcp.foo.test", Srv) answer.name shouldEqual name 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.akka.test", 86400, 10, 40, 5070, "a-double.akka.test") + SRVRecord("service.tcp.foo.test", 86400, 10, 60, 5060, "a-single.foo.test"), + SRVRecord("service.tcp.foo.test", 86400, 10, 40, 5070, "a-double.foo.test") ) } "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.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.foo.test").records.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20")) } "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 } + "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 = { (IO(Dns) ? DnsProtocol.Resolve(name, requestType)).mapTo[DnsProtocol.Resolved].futureValue } } } +object AsyncDnsResolverIntegrationSpec { + lazy val dockerDnsServerPort = SocketUtil.temporaryLocalPort() +} diff --git a/akka-actor-tests/src/test/scala/akka/io/dns/DockerBindDnsService.scala b/akka-actor-tests/src/test/scala/akka/io/dns/DockerBindDnsService.scala new file mode 100644 index 00000000000..d21e4a5f039 --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/io/dns/DockerBindDnsService.scala @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +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) + } +} diff --git a/akka-actor-tests/src/test/scala/akka/io/dns/internal/DnsClientSpec.scala b/akka-actor-tests/src/test/scala/akka/io/dns/internal/DnsClientSpec.scala new file mode 100644 index 00000000000..0934c7bf864 --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/io/dns/internal/DnsClientSpec.scala @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +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) + } + } +} diff --git a/akka-actor-tests/src/test/scala/akka/io/dns/internal/TcpDnsClientSpec.scala b/akka-actor-tests/src/test/scala/akka/io/dns/internal/TcpDnsClientSpec.scala new file mode 100644 index 00000000000..b77fdd73fc7 --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/io/dns/internal/TcpDnsClientSpec.scala @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +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)) + } + } +} diff --git a/akka-actor/src/main/scala/akka/io/dns/internal/DnsClient.scala b/akka-actor/src/main/scala/akka/io/dns/internal/DnsClient.scala index 623221a5831..dd266268a9a 100644 --- a/akka-actor/src/main/scala/akka/io/dns/internal/DnsClient.scala +++ b/akka-actor/src/main/scala/akka/io/dns/internal/DnsClient.scala @@ -7,13 +7,15 @@ package akka.io.dns.internal import java.net.{ InetAddress, InetSocketAddress } 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.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.util.Try +import scala.concurrent.duration._ /** * INTERNAL API @@ -38,13 +40,20 @@ import scala.util.Try 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 = { case Udp.Bound(local) ⇒ - log.debug(s"Bound to UDP address [{}]", local) + log.debug("Bound to UDP address [{}]", local) context.become(ready(sender())) unstashAll() case _: Question4 ⇒ @@ -65,23 +74,23 @@ import scala.util.Try inflightRequests -= id case Question4(id, name) ⇒ log.debug("Resolving [{}] (A)", name) - inflightRequests += (id -> sender()) 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) case Question6(id, name) ⇒ log.debug("Resolving [{}] (AAAA)", name) - inflightRequests += (id -> sender()) 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) case SrvQuestion(id, name) ⇒ log.debug("Resolving [{}] (SRV)", name) - inflightRequests += (id -> sender()) 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) case Udp.CommandFailed(cmd) ⇒ @@ -91,33 +100,51 @@ import scala.util.Try // best effort, don't throw Try { val msg = Message.parse(send.payload) - inflightRequests.get(msg.id).foreach { s ⇒ - s ! Failure(new RuntimeException("Send failed to nameserver")) - inflightRequests -= msg.id + inflightRequests.get(msg.id).foreach { + case (s, _) ⇒ + s ! Failure(new RuntimeException("Send failed to nameserver")) + inflightRequests -= msg.id } } case _ ⇒ log.warning("Dns client failed to send {}", cmd) } 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) - log.debug(s"Decoded: $msg") - // TODO remove me when #25460 is implemented + log.debug("Decoded UDP DNS response [{}]", data) + 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) - val response = Answer(msg.id, recs, additionalRecs) + case response: Answer ⇒ inflightRequests.get(response.id) match { - case Some(reply) ⇒ + case Some((reply, _)) ⇒ reply ! response inflightRequests -= response.id case None ⇒ log.debug("Client for id {} not found. Discarding response.", response.id) } - case Udp.Unbind ⇒ socket ! Udp.Unbind 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") + } } diff --git a/akka-actor/src/main/scala/akka/io/dns/internal/TcpDnsClient.scala b/akka-actor/src/main/scala/akka/io/dns/internal/TcpDnsClient.scala new file mode 100644 index 00000000000..670bcef2120 --- /dev/null +++ b/akka-actor/src/main/scala/akka/io/dns/internal/TcpDnsClient.scala @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +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) + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c2591680ed1..47b8d2e195c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -101,6 +101,9 @@ object Dependencies { // in-memory filesystem for file related tests 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 val metrics = "io.dropwizard.metrics" % "metrics-core" % "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 actorTests = l ++= Seq(Test.junit, Test.scalatest.value, Test.commonsCodec, Test.commonsMath, - Test.mockito, Test.scalacheck.value, Test.jimfs) + val actorTests = l ++= Seq( + 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)