Skip to content

Commit 17575b3

Browse files
committed
Implement correct handling of recursive DNS, close AsyncHttpClient#1329
Motivation: DnsNameResolver does not handle recursive DNS and so fails if you query a DNS server (for example a ROOT dns server) which provides the correct redirect for a domain. Modification: Add support for redirects (a.k.a. handling of AUTHORITY section'). Result: Its now possible to use a DNS server that redirects.
1 parent 3e6c674 commit 17575b3

File tree

6 files changed

+578
-76
lines changed

6 files changed

+578
-76
lines changed

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

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public class DnsNameResolver extends InetNameResolver {
134134
* Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}.
135135
*/
136136
private final DnsCache resolveCache;
137+
private final DnsCache authoritativeDnsServerCache;
137138

138139
private final FastThreadLocal<DnsServerAddressStream> nameServerAddrStream =
139140
new FastThreadLocal<DnsServerAddressStream>() {
@@ -153,8 +154,8 @@ protected DnsServerAddressStream initialValue() throws Exception {
153154
private final HostsFileEntriesResolver hostsFileEntriesResolver;
154155
private final String[] searchDomains;
155156
private final int ndots;
156-
private final boolean cnameFollowARecords;
157-
private final boolean cnameFollowAAAARecords;
157+
private final boolean supportsAAAARecords;
158+
private final boolean supportsARecords;
158159
private final InternetProtocolFamily2 preferredAddressType;
159160
private final DnsRecordType[] resolveRecordTypes;
160161
private final boolean decodeIdn;
@@ -178,9 +179,9 @@ protected DnsServerAddressStream initialValue() throws Exception {
178179
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
179180
* @param searchDomains the list of search domain
180181
* @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)}
182+
* @deprecated use {@link DnsNameResolver#DnsNameResolver(EventLoop, ChannelFactory, DnsServerAddresses, DnsCache,
183+
* DnsCache, long, InternetProtocolFamily2[], boolean, int, boolean, int, boolean,
184+
* HostsFileEntriesResolver, String[], int, boolean)}
184185
*/
185186
@Deprecated
186187
public DnsNameResolver(
@@ -198,11 +199,10 @@ public DnsNameResolver(
198199
HostsFileEntriesResolver hostsFileEntriesResolver,
199200
String[] searchDomains,
200201
int ndots) {
201-
this(eventLoop, channelFactory, nameServerAddresses, resolveCache, queryTimeoutMillis, resolvedAddressTypes,
202-
recursionDesired, maxQueriesPerResolve, traceEnabled, maxPayloadSize, optResourceEnabled,
203-
hostsFileEntriesResolver, searchDomains, ndots, true);
202+
this(eventLoop, channelFactory, nameServerAddresses, resolveCache, NoopDnsCache.INSTANCE, queryTimeoutMillis,
203+
resolvedAddressTypes, recursionDesired, maxQueriesPerResolve, traceEnabled, maxPayloadSize,
204+
optResourceEnabled, hostsFileEntriesResolver, searchDomains, ndots, true);
204205
}
205-
206206
/**
207207
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
208208
*
@@ -212,6 +212,7 @@ public DnsNameResolver(
212212
* this to determine which DNS server should be contacted for the next retry in case
213213
* of failure.
214214
* @param resolveCache the DNS resolved entries cache
215+
* @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain
215216
* @param queryTimeoutMillis timeout of each DNS query in millis
216217
* @param resolvedAddressTypes list of the protocol families
217218
* @param recursionDesired if recursion desired flag must be set
@@ -230,6 +231,7 @@ public DnsNameResolver(
230231
ChannelFactory<? extends DatagramChannel> channelFactory,
231232
DnsServerAddresses nameServerAddresses,
232233
final DnsCache resolveCache,
234+
DnsCache authoritativeDnsServerCache,
233235
long queryTimeoutMillis,
234236
InternetProtocolFamily2[] resolvedAddressTypes,
235237
boolean recursionDesired,
@@ -241,7 +243,6 @@ public DnsNameResolver(
241243
String[] searchDomains,
242244
int ndots,
243245
boolean decodeIdn) {
244-
245246
super(eventLoop);
246247
checkNotNull(channelFactory, "channelFactory");
247248
this.nameServerAddresses = checkNotNull(nameServerAddresses, "nameServerAddresses");
@@ -254,22 +255,23 @@ public DnsNameResolver(
254255
this.optResourceEnabled = optResourceEnabled;
255256
this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
256257
this.resolveCache = checkNotNull(resolveCache, "resolveCache");
258+
this.authoritativeDnsServerCache = checkNotNull(authoritativeDnsServerCache, "authoritativeDnsServerCache");
257259
this.searchDomains = checkNotNull(searchDomains, "searchDomains").clone();
258260
this.ndots = checkPositiveOrZero(ndots, "ndots");
259261
this.decodeIdn = decodeIdn;
260262

261-
boolean cnameFollowARecords = false;
262-
boolean cnameFollowAAAARecords = false;
263+
boolean supportsARecords = false;
264+
boolean supportsAAAARecords = false;
263265
// Use LinkedHashSet to maintain correct ordering.
264266
Set<DnsRecordType> recordTypes = new LinkedHashSet<DnsRecordType>(resolvedAddressTypes.length);
265267
for (InternetProtocolFamily2 family: resolvedAddressTypes) {
266268
switch (family) {
267269
case IPv4:
268-
cnameFollowARecords = true;
270+
supportsARecords = true;
269271
recordTypes.add(DnsRecordType.A);
270272
break;
271273
case IPv6:
272-
cnameFollowAAAARecords = true;
274+
supportsAAAARecords = true;
273275
recordTypes.add(DnsRecordType.AAAA);
274276
break;
275277
default:
@@ -278,9 +280,9 @@ public DnsNameResolver(
278280
}
279281

280282
// One of both must be always true.
281-
assert cnameFollowARecords || cnameFollowAAAARecords;
282-
this.cnameFollowAAAARecords = cnameFollowAAAARecords;
283-
this.cnameFollowARecords = cnameFollowARecords;
283+
assert supportsARecords || supportsAAAARecords;
284+
this.supportsAAAARecords = supportsAAAARecords;
285+
this.supportsARecords = supportsARecords;
284286
resolveRecordTypes = recordTypes.toArray(new DnsRecordType[recordTypes.size()]);
285287
preferredAddressType = resolvedAddressTypes[0];
286288

@@ -308,13 +310,25 @@ public void operationComplete(ChannelFuture future) throws Exception {
308310
});
309311
}
310312

313+
// Only here to override in unit tests.
314+
int dnsRedirectPort(@SuppressWarnings("unused") InetAddress server) {
315+
return DnsServerAddresses.DNS_PORT;
316+
}
317+
311318
/**
312319
* Returns the resolution cache.
313320
*/
314321
public DnsCache resolveCache() {
315322
return resolveCache;
316323
}
317324

325+
/**
326+
* Returns the cache used for authoritative DNS servers for a domain.
327+
*/
328+
public DnsCache authoritativeDnsServerCache() {
329+
return authoritativeDnsServerCache;
330+
}
331+
318332
/**
319333
* Returns the timeout of each DNS query performed by this resolver (in milliseconds).
320334
* The default value is 5 seconds.
@@ -344,12 +358,12 @@ final int ndots() {
344358
return ndots;
345359
}
346360

347-
final boolean isCnameFollowAAAARecords() {
348-
return cnameFollowAAAARecords;
361+
final boolean supportsAAAARecords() {
362+
return supportsAAAARecords;
349363
}
350364

351-
final boolean isCnameFollowARecords() {
352-
return cnameFollowARecords;
365+
final boolean supportsARecords() {
366+
return supportsARecords;
353367
}
354368

355369
final InternetProtocolFamily2 preferredAddressType() {

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public final class DnsNameResolverBuilder {
4040
private ChannelFactory<? extends DatagramChannel> channelFactory;
4141
private DnsServerAddresses nameServerAddresses = DnsServerAddresses.defaultAddresses();
4242
private DnsCache resolveCache;
43+
private DnsCache authoritativeDnsServerCache;
4344
private Integer minTtl;
4445
private Integer maxTtl;
4546
private Integer negativeTtl;
@@ -109,6 +110,17 @@ public DnsNameResolverBuilder resolveCache(DnsCache resolveCache) {
109110
return this;
110111
}
111112

113+
/**
114+
* Sets the cache for authoritive NS servers
115+
*
116+
* @param authoritativeDnsServerCache the authoritive NS servers cache
117+
* @return {@code this}
118+
*/
119+
public DnsNameResolverBuilder authoritativeDnsServerCache(DnsCache authoritativeDnsServerCache) {
120+
this.authoritativeDnsServerCache = authoritativeDnsServerCache;
121+
return this;
122+
}
123+
112124
/**
113125
* Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS
114126
* resource record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL,
@@ -331,6 +343,10 @@ public DnsNameResolverBuilder ndots(int ndots) {
331343
return this;
332344
}
333345

346+
private DnsCache newCache() {
347+
return new DefaultDnsCache(intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE), intValue(negativeTtl, 0));
348+
}
349+
334350
/**
335351
* Set if domain / host names should be decoded to unicode when received.
336352
* See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
@@ -349,19 +365,23 @@ public DnsNameResolverBuilder decodeIdn(boolean decodeIdn) {
349365
* @return a {@link DnsNameResolver}
350366
*/
351367
public DnsNameResolver build() {
352-
353368
if (resolveCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) {
354369
throw new IllegalStateException("resolveCache and TTLs are mutually exclusive");
355370
}
356371

357-
DnsCache cache = resolveCache != null ? resolveCache :
358-
new DefaultDnsCache(intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE), intValue(negativeTtl, 0));
372+
if (authoritativeDnsServerCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) {
373+
throw new IllegalStateException("authoritativeDnsServerCache and TTLs are mutually exclusive");
374+
}
359375

376+
DnsCache resolveCache = this.resolveCache != null ? this.resolveCache : newCache();
377+
DnsCache authoritativeDnsServerCache = this.authoritativeDnsServerCache != null ?
378+
this.authoritativeDnsServerCache : newCache();
360379
return new DnsNameResolver(
361380
eventLoop,
362381
channelFactory,
363382
nameServerAddresses,
364-
cache,
383+
resolveCache,
384+
authoritativeDnsServerCache,
365385
queryTimeoutMillis,
366386
resolvedAddressTypes,
367387
recursionDesired,

0 commit comments

Comments
 (0)