Skip to content

Commit c04ab5b

Browse files
committed
Automatically decode DNS domain name to unicode, close AsyncHttpClient#1338
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.
1 parent 3712f33 commit c04ab5b

File tree

4 files changed

+148
-36
lines changed

4 files changed

+148
-36
lines changed

netty-bp/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
import io.netty.handler.codec.dns.DnsResponse;
4040
import io.netty.resolver.HostsFileEntriesResolver;
4141
import io.netty.resolver.InetNameResolver;
42+
import io.netty.util.NetUtil;
4243
import io.netty.util.NetUtil2;
4344
import io.netty.util.ReferenceCountUtil;
4445
import io.netty.util.concurrent.FastThreadLocal;
4546
import io.netty.util.concurrent.Future;
4647
import io.netty.util.concurrent.Promise;
4748
import io.netty.util.internal.EmptyArrays;
4849
import io.netty.util.internal.PlatformDependent;
50+
import io.netty.util.internal.StringUtil;
4951
import io.netty.util.internal.StringUtil2;
5052
import io.netty.util.internal.UnstableApi;
5153
import io.netty.util.internal.logging.InternalLogger;
@@ -81,19 +83,19 @@ public class DnsNameResolver extends InetNameResolver {
8183
static final String[] DEFAULT_SEACH_DOMAINS;
8284

8385
static {
84-
if (NetUtil2.isIpV4StackPreferred()) {
86+
if (NetUtil.isIpV4StackPreferred()) {
8587
DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily2[] { InternetProtocolFamily2.IPv4 };
86-
LOCALHOST_ADDRESS = NetUtil2.LOCALHOST4;
88+
LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
8789
} else {
8890
DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily2[2];
8991
if (NetUtil2.isIpV6AddressesPreferred()) {
9092
DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily2.IPv6;
9193
DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily2.IPv4;
92-
LOCALHOST_ADDRESS = NetUtil2.LOCALHOST6;
94+
LOCALHOST_ADDRESS = NetUtil.LOCALHOST6;
9395
} else {
9496
DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily2.IPv4;
9597
DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily2.IPv6;
96-
LOCALHOST_ADDRESS = NetUtil2.LOCALHOST4;
98+
LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
9799
}
98100
}
99101
}
@@ -155,6 +157,7 @@ protected DnsServerAddressStream initialValue() throws Exception {
155157
private final boolean cnameFollowAAAARecords;
156158
private final InternetProtocolFamily2 preferredAddressType;
157159
private final DnsRecordType[] resolveRecordTypes;
160+
private final boolean decodeIdn;
158161

159162
/**
160163
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
@@ -175,7 +178,11 @@ protected DnsServerAddressStream initialValue() throws Exception {
175178
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
176179
* @param searchDomains the list of search domain
177180
* @param ndots the ndots value
181+
* @deprecated use {@link #DnsNameResolver(EventLoop, ChannelFactory, DnsServerAddresses, DnsCache, long,
182+
* InternetProtocolFamily2[], boolean, int, boolean, int,
183+
* boolean, HostsFileEntriesResolver, String[], int, boolean)}
178184
*/
185+
@Deprecated
179186
public DnsNameResolver(
180187
EventLoop eventLoop,
181188
ChannelFactory<? extends DatagramChannel> channelFactory,
@@ -191,6 +198,49 @@ public DnsNameResolver(
191198
HostsFileEntriesResolver hostsFileEntriesResolver,
192199
String[] searchDomains,
193200
int ndots) {
201+
this(eventLoop, channelFactory, nameServerAddresses, resolveCache, queryTimeoutMillis, resolvedAddressTypes,
202+
recursionDesired, maxQueriesPerResolve, traceEnabled, maxPayloadSize, optResourceEnabled,
203+
hostsFileEntriesResolver, searchDomains, ndots, true);
204+
}
205+
206+
/**
207+
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
208+
*
209+
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
210+
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
211+
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
212+
* this to determine which DNS server should be contacted for the next retry in case
213+
* of failure.
214+
* @param resolveCache the DNS resolved entries cache
215+
* @param queryTimeoutMillis timeout of each DNS query in millis
216+
* @param resolvedAddressTypes list of the protocol families
217+
* @param recursionDesired if recursion desired flag must be set
218+
* @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution
219+
* @param traceEnabled if trace is enabled
220+
* @param maxPayloadSize the capacity of the datagram packet buffer
221+
* @param optResourceEnabled if automatic inclusion of a optional records is enabled
222+
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
223+
* @param searchDomains the list of search domain
224+
* @param ndots the ndots value
225+
* @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received.
226+
* See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
227+
*/
228+
public DnsNameResolver(
229+
EventLoop eventLoop,
230+
ChannelFactory<? extends DatagramChannel> channelFactory,
231+
DnsServerAddresses nameServerAddresses,
232+
final DnsCache resolveCache,
233+
long queryTimeoutMillis,
234+
InternetProtocolFamily2[] resolvedAddressTypes,
235+
boolean recursionDesired,
236+
int maxQueriesPerResolve,
237+
boolean traceEnabled,
238+
int maxPayloadSize,
239+
boolean optResourceEnabled,
240+
HostsFileEntriesResolver hostsFileEntriesResolver,
241+
String[] searchDomains,
242+
int ndots,
243+
boolean decodeIdn) {
194244

195245
super(eventLoop);
196246
checkNotNull(channelFactory, "channelFactory");
@@ -206,6 +256,7 @@ public DnsNameResolver(
206256
this.resolveCache = checkNotNull(resolveCache, "resolveCache");
207257
this.searchDomains = checkNotNull(searchDomains, "searchDomains").clone();
208258
this.ndots = checkPositiveOrZero(ndots, "ndots");
259+
this.decodeIdn = decodeIdn;
209260

210261
boolean cnameFollowARecords = false;
211262
boolean cnameFollowAAAARecords = false;
@@ -309,6 +360,10 @@ final DnsRecordType[] resolveRecordTypes() {
309360
return resolveRecordTypes;
310361
}
311362

363+
final boolean isDecodeIdn() {
364+
return decodeIdn;
365+
}
366+
312367
/**
313368
* Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
314369
* The default value is {@code true}.
@@ -503,7 +558,7 @@ private static void validateAdditional(DnsRecord record, boolean validateType) {
503558
@Override
504559
protected final InetAddress loopbackAddress() {
505560
return preferredAddressType() == InternetProtocolFamily2.IPv4 ?
506-
NetUtil2.LOCALHOST4 : NetUtil2.LOCALHOST6;
561+
NetUtil.LOCALHOST4 : NetUtil.LOCALHOST6;
507562
}
508563

509564
/**
@@ -514,7 +569,7 @@ protected void doResolve(String inetHost,
514569
DnsRecord[] additionals,
515570
Promise<InetAddress> promise,
516571
DnsCache resolveCache) throws Exception {
517-
final byte[] bytes = NetUtil2.createByteArrayFromIpAddressString(inetHost);
572+
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
518573
if (bytes != null) {
519574
// The inetHost is actually an ipaddress.
520575
promise.setSuccess(InetAddress.getByAddress(bytes));
@@ -639,7 +694,7 @@ protected void doResolveAll(String inetHost,
639694
DnsRecord[] additionals,
640695
Promise<List<InetAddress>> promise,
641696
DnsCache resolveCache) throws Exception {
642-
final byte[] bytes = NetUtil2.createByteArrayFromIpAddressString(inetHost);
697+
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
643698
if (bytes != null) {
644699
// The unresolvedAddress was created via a String that contains an ipaddress.
645700
promise.setSuccess(Collections.singletonList(InetAddress.getByAddress(bytes)));

netty-bp/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,25 @@
1515
*/
1616
package io.netty.resolver.dns;
1717

18-
import static io.netty.util.internal.ObjectUtil2.intValue;
18+
import static io.netty.util.internal.ObjectUtil.intValue;
1919

2020
import io.netty.bootstrap.ChannelFactory;
2121
import io.netty.channel.EventLoop;
2222
import io.netty.channel.ReflectiveChannelFactory;
2323
import io.netty.channel.socket.DatagramChannel;
2424
import io.netty.channel.socket.InternetProtocolFamily2;
2525
import io.netty.resolver.HostsFileEntriesResolver;
26-
import io.netty.util.internal.InternalThreadLocalMap;
26+
import io.netty.util.internal.UnstableApi;
2727

28+
import java.util.ArrayList;
2829
import java.util.List;
2930

3031
import static io.netty.util.internal.ObjectUtil.checkNotNull;
3132

3233
/**
3334
* A {@link DnsNameResolver} builder.
3435
*/
36+
@UnstableApi
3537
public final class DnsNameResolverBuilder {
3638

3739
private final EventLoop eventLoop;
@@ -51,6 +53,7 @@ public final class DnsNameResolverBuilder {
5153
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
5254
private String[] searchDomains = DnsNameResolver.DEFAULT_SEACH_DOMAINS;
5355
private int ndots = 1;
56+
private boolean decodeIdn = true;
5457

5558
/**
5659
* Creates a new builder.
@@ -158,8 +161,8 @@ public DnsNameResolverBuilder queryTimeoutMillis(long queryTimeoutMillis) {
158161
public DnsNameResolverBuilder resolvedAddressTypes(InternetProtocolFamily2... resolvedAddressTypes) {
159162
checkNotNull(resolvedAddressTypes, "resolvedAddressTypes");
160163

161-
final List<InternetProtocolFamily2> list =
162-
InternalThreadLocalMap.get().arrayList(InternetProtocolFamily2.values().length);
164+
final List<InternetProtocolFamily2> list = new ArrayList<InternetProtocolFamily2>(
165+
InternetProtocolFamily2.values().length);
163166

164167
for (InternetProtocolFamily2 f : resolvedAddressTypes) {
165168
if (f == null) {
@@ -195,8 +198,8 @@ public DnsNameResolverBuilder resolvedAddressTypes(InternetProtocolFamily2... re
195198
public DnsNameResolverBuilder resolvedAddressTypes(Iterable<InternetProtocolFamily2> resolvedAddressTypes) {
196199
checkNotNull(resolvedAddressTypes, "resolveAddressTypes");
197200

198-
final List<InternetProtocolFamily2> list =
199-
InternalThreadLocalMap.get().arrayList(InternetProtocolFamily2.values().length);
201+
final List<InternetProtocolFamily2> list = new ArrayList<InternetProtocolFamily2>(
202+
InternetProtocolFamily2.values().length);
200203

201204
for (InternetProtocolFamily2 f : resolvedAddressTypes) {
202205
if (f == null) {
@@ -297,8 +300,7 @@ public DnsNameResolverBuilder hostsFileEntriesResolver(HostsFileEntriesResolver
297300
public DnsNameResolverBuilder searchDomains(Iterable<String> searchDomains) {
298301
checkNotNull(searchDomains, "searchDomains");
299302

300-
final List<String> list =
301-
InternalThreadLocalMap.get().arrayList(4);
303+
final List<String> list = new ArrayList<String>(4);
302304

303305
for (String f : searchDomains) {
304306
if (f == null) {
@@ -329,6 +331,18 @@ public DnsNameResolverBuilder ndots(int ndots) {
329331
return this;
330332
}
331333

334+
/**
335+
* Set if domain / host names should be decoded to unicode when received.
336+
* See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
337+
*
338+
* @param decodeIdn if should get decoded
339+
* @return {@code this}
340+
*/
341+
public DnsNameResolverBuilder decodeIdn(boolean decodeIdn) {
342+
this.decodeIdn = decodeIdn;
343+
return this;
344+
}
345+
332346
/**
333347
* Returns a new {@link DnsNameResolver} instance.
334348
*
@@ -357,6 +371,7 @@ public DnsNameResolver build() {
357371
optResourceEnabled,
358372
hostsFileEntriesResolver,
359373
searchDomains,
360-
ndots);
374+
ndots,
375+
decodeIdn);
361376
}
362377
}

netty-bp/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverContext.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.netty.util.internal.StringUtil;
3838
import io.netty.util.internal.StringUtil2;
3939

40+
import java.net.IDN;
4041
import java.net.Inet4Address;
4142
import java.net.Inet6Address;
4243
import java.net.InetAddress;
@@ -273,7 +274,8 @@ private void onResponseAorAAAA(
273274

274275
final InetAddress resolved;
275276
try {
276-
resolved = InetAddress.getByAddress(hostname, addrBytes);
277+
resolved = InetAddress.getByAddress(
278+
parent.isDecodeIdn() ? IDN.toUnicode(hostname) : hostname, addrBytes);
277279
} catch (UnknownHostException e) {
278280
// Should never reach here.
279281
throw new Error(e);

0 commit comments

Comments
 (0)