Automatically decode DNS domain name to unicode
Motivation: DnsNameResolver will return the domain / host name as ascii code using punycode (https://tools.ietf.org/html/rfc3492). This is different to what the JDK does which always convert it to unicode. We should do the same by default but allow to also not do it. Modifications: - Add new builder method on DnsNameResolverBuilder which allow to disable / enable converting. Default is to convert just like the JDK does. - Add unit tests for it. Result: DnsNameResolver and JDK impl behave the same way.
This commit is contained in:
parent
ead9938980
commit
0d5b665fba
|
@ -155,6 +155,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||||
private final boolean cnameFollowAAAARecords;
|
private final boolean cnameFollowAAAARecords;
|
||||||
private final InternetProtocolFamily preferredAddressType;
|
private final InternetProtocolFamily preferredAddressType;
|
||||||
private final DnsRecordType[] resolveRecordTypes;
|
private final DnsRecordType[] resolveRecordTypes;
|
||||||
|
private final boolean decodeIdn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
||||||
|
@ -175,7 +176,11 @@ public class DnsNameResolver extends InetNameResolver {
|
||||||
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
|
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
|
||||||
* @param searchDomains the list of search domain
|
* @param searchDomains the list of search domain
|
||||||
* @param ndots the ndots value
|
* @param ndots the ndots value
|
||||||
|
* @deprecated use {@link #DnsNameResolver(EventLoop, ChannelFactory, DnsServerAddresses, DnsCache, long,
|
||||||
|
* InternetProtocolFamily[], boolean, int, boolean, int,
|
||||||
|
* boolean, HostsFileEntriesResolver, String[], int, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public DnsNameResolver(
|
public DnsNameResolver(
|
||||||
EventLoop eventLoop,
|
EventLoop eventLoop,
|
||||||
ChannelFactory<? extends DatagramChannel> channelFactory,
|
ChannelFactory<? extends DatagramChannel> channelFactory,
|
||||||
|
@ -191,6 +196,49 @@ public class DnsNameResolver extends InetNameResolver {
|
||||||
HostsFileEntriesResolver hostsFileEntriesResolver,
|
HostsFileEntriesResolver hostsFileEntriesResolver,
|
||||||
String[] searchDomains,
|
String[] searchDomains,
|
||||||
int ndots) {
|
int ndots) {
|
||||||
|
this(eventLoop, channelFactory, nameServerAddresses, resolveCache, queryTimeoutMillis, resolvedAddressTypes,
|
||||||
|
recursionDesired, maxQueriesPerResolve, traceEnabled, maxPayloadSize, optResourceEnabled,
|
||||||
|
hostsFileEntriesResolver, searchDomains, ndots, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
||||||
|
*
|
||||||
|
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
|
||||||
|
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
|
||||||
|
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
||||||
|
* this to determine which DNS server should be contacted for the next retry in case
|
||||||
|
* of failure.
|
||||||
|
* @param resolveCache the DNS resolved entries cache
|
||||||
|
* @param queryTimeoutMillis timeout of each DNS query in millis
|
||||||
|
* @param resolvedAddressTypes list of the protocol families
|
||||||
|
* @param recursionDesired if recursion desired flag must be set
|
||||||
|
* @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution
|
||||||
|
* @param traceEnabled if trace is enabled
|
||||||
|
* @param maxPayloadSize the capacity of the datagram packet buffer
|
||||||
|
* @param optResourceEnabled if automatic inclusion of a optional records is enabled
|
||||||
|
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
|
||||||
|
* @param searchDomains the list of search domain
|
||||||
|
* @param ndots the ndots value
|
||||||
|
* @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received.
|
||||||
|
* See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
|
||||||
|
*/
|
||||||
|
public DnsNameResolver(
|
||||||
|
EventLoop eventLoop,
|
||||||
|
ChannelFactory<? extends DatagramChannel> channelFactory,
|
||||||
|
DnsServerAddresses nameServerAddresses,
|
||||||
|
final DnsCache resolveCache,
|
||||||
|
long queryTimeoutMillis,
|
||||||
|
InternetProtocolFamily[] resolvedAddressTypes,
|
||||||
|
boolean recursionDesired,
|
||||||
|
int maxQueriesPerResolve,
|
||||||
|
boolean traceEnabled,
|
||||||
|
int maxPayloadSize,
|
||||||
|
boolean optResourceEnabled,
|
||||||
|
HostsFileEntriesResolver hostsFileEntriesResolver,
|
||||||
|
String[] searchDomains,
|
||||||
|
int ndots,
|
||||||
|
boolean decodeIdn) {
|
||||||
|
|
||||||
super(eventLoop);
|
super(eventLoop);
|
||||||
checkNotNull(channelFactory, "channelFactory");
|
checkNotNull(channelFactory, "channelFactory");
|
||||||
|
@ -206,6 +254,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||||
this.resolveCache = checkNotNull(resolveCache, "resolveCache");
|
this.resolveCache = checkNotNull(resolveCache, "resolveCache");
|
||||||
this.searchDomains = checkNotNull(searchDomains, "searchDomains").clone();
|
this.searchDomains = checkNotNull(searchDomains, "searchDomains").clone();
|
||||||
this.ndots = checkPositiveOrZero(ndots, "ndots");
|
this.ndots = checkPositiveOrZero(ndots, "ndots");
|
||||||
|
this.decodeIdn = decodeIdn;
|
||||||
|
|
||||||
boolean cnameFollowARecords = false;
|
boolean cnameFollowARecords = false;
|
||||||
boolean cnameFollowAAAARecords = false;
|
boolean cnameFollowAAAARecords = false;
|
||||||
|
@ -309,6 +358,10 @@ public class DnsNameResolver extends InetNameResolver {
|
||||||
return resolveRecordTypes;
|
return resolveRecordTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean isDecodeIdn() {
|
||||||
|
return decodeIdn;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
|
* Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
|
||||||
* The default value is {@code true}.
|
* The default value is {@code true}.
|
||||||
|
|
|
@ -53,6 +53,7 @@ public final class DnsNameResolverBuilder {
|
||||||
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
||||||
private String[] searchDomains = DnsNameResolver.DEFAULT_SEACH_DOMAINS;
|
private String[] searchDomains = DnsNameResolver.DEFAULT_SEACH_DOMAINS;
|
||||||
private int ndots = 1;
|
private int ndots = 1;
|
||||||
|
private boolean decodeIdn = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new builder.
|
* Creates a new builder.
|
||||||
|
@ -330,6 +331,18 @@ public final class DnsNameResolverBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if domain / host names should be decoded to unicode when received.
|
||||||
|
* See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
|
||||||
|
*
|
||||||
|
* @param decodeIdn if should get decoded
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder decodeIdn(boolean decodeIdn) {
|
||||||
|
this.decodeIdn = decodeIdn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new {@link DnsNameResolver} instance.
|
* Returns a new {@link DnsNameResolver} instance.
|
||||||
*
|
*
|
||||||
|
@ -358,6 +371,7 @@ public final class DnsNameResolverBuilder {
|
||||||
optResourceEnabled,
|
optResourceEnabled,
|
||||||
hostsFileEntriesResolver,
|
hostsFileEntriesResolver,
|
||||||
searchDomains,
|
searchDomains,
|
||||||
ndots);
|
ndots,
|
||||||
|
decodeIdn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import io.netty.util.concurrent.FutureListener;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
|
import java.net.IDN;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -272,7 +273,8 @@ abstract class DnsNameResolverContext<T> {
|
||||||
|
|
||||||
final InetAddress resolved;
|
final InetAddress resolved;
|
||||||
try {
|
try {
|
||||||
resolved = InetAddress.getByAddress(hostname, addrBytes);
|
resolved = InetAddress.getByAddress(
|
||||||
|
parent.isDecodeIdn() ? IDN.toUnicode(hostname) : hostname, addrBytes);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
// Should never reach here.
|
// Should never reach here.
|
||||||
throw new Error(e);
|
throw new Error(e);
|
||||||
|
|
|
@ -170,6 +170,20 @@ public class DnsNameResolverTest {
|
||||||
"blogspot.in",
|
"blogspot.in",
|
||||||
"localhost")));
|
"localhost")));
|
||||||
|
|
||||||
|
private static final Map<String, String> DOMAINS_PUNYCODE = new HashMap<String, String>();
|
||||||
|
static {
|
||||||
|
DOMAINS_PUNYCODE.put("büchner.de", "xn--bchner-3ya.de");
|
||||||
|
DOMAINS_PUNYCODE.put("müller.de", "xn--mller-kva.de");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<String> DOMAINS_ALL;
|
||||||
|
static {
|
||||||
|
Set<String> all = new HashSet<String>(DOMAINS.size() + DOMAINS_PUNYCODE.size());
|
||||||
|
all.addAll(DOMAINS);
|
||||||
|
all.addAll(DOMAINS_PUNYCODE.values());
|
||||||
|
DOMAINS_ALL = Collections.unmodifiableSet(all);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of the domain names to exclude from {@link #testResolveAorAAAA()}.
|
* The list of the domain names to exclude from {@link #testResolveAorAAAA()}.
|
||||||
*/
|
*/
|
||||||
|
@ -238,17 +252,22 @@ public class DnsNameResolverTest {
|
||||||
StringUtil.EMPTY_STRING);
|
StringUtil.EMPTY_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final TestDnsServer dnsServer = new TestDnsServer(DOMAINS);
|
private static final TestDnsServer dnsServer = new TestDnsServer(DOMAINS_ALL);
|
||||||
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
||||||
|
|
||||||
private static DnsNameResolverBuilder newResolver() {
|
private static DnsNameResolverBuilder newResolver(boolean decodeToUnicode) {
|
||||||
return new DnsNameResolverBuilder(group.next())
|
return new DnsNameResolverBuilder(group.next())
|
||||||
.channelType(NioDatagramChannel.class)
|
.channelType(NioDatagramChannel.class)
|
||||||
.nameServerAddresses(DnsServerAddresses.singleton(dnsServer.localAddress()))
|
.nameServerAddresses(DnsServerAddresses.singleton(dnsServer.localAddress()))
|
||||||
.maxQueriesPerResolve(1)
|
.maxQueriesPerResolve(1)
|
||||||
|
.decodeIdn(decodeToUnicode)
|
||||||
.optResourceEnabled(false);
|
.optResourceEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DnsNameResolverBuilder newResolver() {
|
||||||
|
return newResolver(true);
|
||||||
|
}
|
||||||
|
|
||||||
private static DnsNameResolverBuilder newResolver(InternetProtocolFamily... resolvedAddressTypes) {
|
private static DnsNameResolverBuilder newResolver(InternetProtocolFamily... resolvedAddressTypes) {
|
||||||
return newResolver()
|
return newResolver()
|
||||||
.resolvedAddressTypes(resolvedAddressTypes);
|
.resolvedAddressTypes(resolvedAddressTypes);
|
||||||
|
@ -558,6 +577,28 @@ public class DnsNameResolverTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveDecodeUnicode() {
|
||||||
|
testResolveUnicode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveNotDecodeUnicode() {
|
||||||
|
testResolveUnicode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testResolveUnicode(boolean decode) {
|
||||||
|
DnsNameResolver resolver = newResolver(decode).build();
|
||||||
|
try {
|
||||||
|
for (Entry<String, String> entries : DOMAINS_PUNYCODE.entrySet()) {
|
||||||
|
InetAddress address = resolver.resolve(entries.getKey()).syncUninterruptibly().getNow();
|
||||||
|
assertEquals(decode ? entries.getKey() : entries.getValue(), address.getHostName());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void resolve(DnsNameResolver resolver, Map<String, Future<InetAddress>> futures, String hostname) {
|
private static void resolve(DnsNameResolver resolver, Map<String, Future<InetAddress>> futures, String hostname) {
|
||||||
futures.put(hostname, resolver.resolve(hostname));
|
futures.put(hostname, resolver.resolve(hostname));
|
||||||
}
|
}
|
||||||
|
@ -568,5 +609,4 @@ public class DnsNameResolverTest {
|
||||||
String hostname) throws Exception {
|
String hostname) throws Exception {
|
||||||
futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX)));
|
futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user