From fd59579790b7835077f58820b352677973051eaf Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Thu, 9 Aug 2018 12:27:57 +0900 Subject: [PATCH] =dns #25451 resolve raw IP addresses in async-dns --- .../dns/AsyncDnsResolverIntegrationSpec.scala | 21 ++++++++++++++++- .../io/dns/internal/AsyncDnsResolver.scala | 23 +++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) 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 f4e5d803bf..880590ba0f 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 @@ -90,7 +90,9 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( implicit val timeout = Timeout(duration) "Resolver" must { - pending + + pending // PENDING since needs `bind` server to be running to test end-to-end + "resolve single A record" in { val name = "a-single.akka.test" val answer = resolve(name, DnsProtocol.Ip(ipv6 = false)) @@ -181,6 +183,23 @@ class AsyncDnsResolverIntegrationSpec extends AkkaSpec( ) } + "not hang when resolving raw IP address" in { + val name = "127.0.0.1" + val answer = resolve(name) + answer.name shouldEqual name + answer.results.collect { case r: ARecord ⇒ r }.toSet shouldEqual Set( + ARecord("127.0.0.1", Int.MaxValue, InetAddress.getByName("127.0.0.1")) + ) + } + "not hang when resolving raw IPv6 address" in { + val name = "1:2:3:0:0:0:0:0" + val answer = resolve(name) + answer.name shouldEqual name + answer.results.collect { case r: ARecord ⇒ r }.toSet shouldEqual Set( + ARecord("1:2:3:0:0:0:0:0", Int.MaxValue, InetAddress.getByName("1:2:3:0:0:0:0:0")) + ) + } + "resolve same address twice" in { resolve("a-single.akka.test").results.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20")) resolve("a-single.akka.test").results.map(_.asInstanceOf[ARecord].ip) shouldEqual Seq(InetAddress.getByName("192.168.1.20")) diff --git a/akka-actor/src/main/scala/akka/io/dns/internal/AsyncDnsResolver.scala b/akka-actor/src/main/scala/akka/io/dns/internal/AsyncDnsResolver.scala index cef893a989..65b98cc490 100644 --- a/akka-actor/src/main/scala/akka/io/dns/internal/AsyncDnsResolver.scala +++ b/akka-actor/src/main/scala/akka/io/dns/internal/AsyncDnsResolver.scala @@ -4,13 +4,14 @@ package akka.io.dns.internal -import java.net.InetSocketAddress +import java.net.{ InetAddress, InetSocketAddress } +import java.nio.charset.StandardCharsets import akka.actor.{ Actor, ActorLogging, ActorRef, ActorRefFactory, Props } import akka.annotation.InternalApi import akka.io.dns.DnsProtocol.{ Ip, RequestType, Srv } import akka.io.dns.internal.DnsClient._ -import akka.io.dns.{ DnsProtocol, DnsSettings, ResourceRecord } +import akka.io.dns.{ ARecord, DnsProtocol, DnsSettings, ResourceRecord } import akka.pattern.{ ask, pipe } import akka.util.{ Helpers, Timeout } @@ -49,7 +50,8 @@ private[io] final class AsyncDnsResolver( override def receive: Receive = { case DnsProtocol.Resolve(name, _) if isInetAddress(name) ⇒ - log.warning("Tried to resolve ip [{}]. Ignoring.", name) + log.warning("Tried to resolve ip [{}], assuming resolved.", name) + alreadyResolvedIp(name) pipeTo sender() case DnsProtocol.Resolve(name, mode) ⇒ resolve(name, mode, resolvers) pipeTo sender() } @@ -66,6 +68,18 @@ private[io] final class AsyncDnsResolver( } } + private def alreadyResolvedIp(knownToBeIp: String): Future[DnsProtocol.Resolved] = { + if (isInetAddress(knownToBeIp)) + Future { + val address = InetAddress.getByName(knownToBeIp) // only checks validity, since known to be IP address + val record = ARecord(knownToBeIp, Int.MaxValue, address) + DnsProtocol.Resolved(knownToBeIp, record :: Nil) + } + else + Future.failed(new IllegalArgumentException("Attempted to emit Resolved for known-to-be IP address, " + + s"yet argument was not an IP address, was: ${knownToBeIp}")) + } + private def sendQuestion(resolver: ActorRef, message: DnsQuestion): Future[Seq[ResourceRecord]] = { val result = (resolver ? message).mapTo[Answer].map(_.rrs) result.onFailure { @@ -142,7 +156,8 @@ private[io] object AsyncDnsResolver { """^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$""".r private def isInetAddress(name: String): Boolean = - ipv4Address.findAllMatchIn(name).nonEmpty || ipv6Address.findAllMatchIn(name).nonEmpty + ipv4Address.findAllMatchIn(name).nonEmpty || + ipv6Address.findAllMatchIn(name).nonEmpty private val Empty = Future.successful(immutable.Seq.empty[ResourceRecord])