View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.resolver.dns;
17  
18  import io.netty.bootstrap.Bootstrap;
19  import io.netty.buffer.ByteBuf;
20  import io.netty.buffer.Unpooled;
21  import io.netty.channel.AddressedEnvelope;
22  import io.netty.channel.Channel;
23  import io.netty.channel.ChannelFactory;
24  import io.netty.channel.ChannelFuture;
25  import io.netty.channel.ChannelFutureListener;
26  import io.netty.channel.ChannelHandler;
27  import io.netty.channel.ChannelHandlerAdapter;
28  import io.netty.channel.ChannelHandlerContext;
29  import io.netty.channel.ChannelInboundHandlerAdapter;
30  import io.netty.channel.ChannelInitializer;
31  import io.netty.channel.ChannelOption;
32  import io.netty.channel.EventLoop;
33  import io.netty.channel.FixedRecvByteBufAllocator;
34  import io.netty.channel.socket.DatagramChannel;
35  import io.netty.channel.socket.DatagramPacket;
36  import io.netty.channel.socket.InternetProtocolFamily;
37  import io.netty.channel.socket.SocketChannel;
38  import io.netty.handler.codec.CorruptedFrameException;
39  import io.netty.handler.codec.dns.DatagramDnsQueryEncoder;
40  import io.netty.handler.codec.dns.DatagramDnsResponse;
41  import io.netty.handler.codec.dns.DatagramDnsResponseDecoder;
42  import io.netty.handler.codec.dns.DefaultDnsRawRecord;
43  import io.netty.handler.codec.dns.DnsQuestion;
44  import io.netty.handler.codec.dns.DnsRawRecord;
45  import io.netty.handler.codec.dns.DnsRecord;
46  import io.netty.handler.codec.dns.DnsRecordType;
47  import io.netty.handler.codec.dns.DnsResponse;
48  import io.netty.resolver.DefaultHostsFileEntriesResolver;
49  import io.netty.resolver.HostsFileEntries;
50  import io.netty.resolver.HostsFileEntriesResolver;
51  import io.netty.resolver.InetNameResolver;
52  import io.netty.resolver.ResolvedAddressTypes;
53  import io.netty.util.AttributeKey;
54  import io.netty.util.NetUtil;
55  import io.netty.util.ReferenceCountUtil;
56  import io.netty.util.concurrent.EventExecutor;
57  import io.netty.util.concurrent.Future;
58  import io.netty.util.concurrent.FutureListener;
59  import io.netty.util.concurrent.GenericFutureListener;
60  import io.netty.util.concurrent.Promise;
61  import io.netty.util.concurrent.PromiseNotifier;
62  import io.netty.util.internal.EmptyArrays;
63  import io.netty.util.internal.PlatformDependent;
64  import io.netty.util.internal.StringUtil;
65  import io.netty.util.internal.logging.InternalLogger;
66  import io.netty.util.internal.logging.InternalLoggerFactory;
67  
68  import java.lang.reflect.Method;
69  import java.net.IDN;
70  import java.net.Inet4Address;
71  import java.net.Inet6Address;
72  import java.net.InetAddress;
73  import java.net.InetSocketAddress;
74  import java.net.NetworkInterface;
75  import java.net.SocketAddress;
76  import java.net.UnknownHostException;
77  import java.util.ArrayList;
78  import java.util.Arrays;
79  import java.util.Collection;
80  import java.util.Collections;
81  import java.util.Comparator;
82  import java.util.Enumeration;
83  import java.util.HashMap;
84  import java.util.Iterator;
85  import java.util.List;
86  import java.util.Map;
87  import java.util.concurrent.TimeUnit;
88  
89  import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT;
90  import static io.netty.util.internal.ObjectUtil.checkNotNull;
91  import static io.netty.util.internal.ObjectUtil.checkPositive;
92  
93  /**
94   * A DNS-based {@link InetNameResolver}.
95   */
96  public class DnsNameResolver extends InetNameResolver {
97      /**
98       * An attribute used to mark all channels created by the {@link DnsNameResolver}.
99       */
100     public static final AttributeKey<Boolean> DNS_PIPELINE_ATTRIBUTE =
101             AttributeKey.newInstance("io.netty.resolver.dns.pipeline");
102 
103     private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class);
104     private static final String LOCALHOST = "localhost";
105     private static final String WINDOWS_HOST_NAME;
106     private static final InetAddress LOCALHOST_ADDRESS;
107     private static final DnsRecord[] EMPTY_ADDITIONALS = new DnsRecord[0];
108     private static final DnsRecordType[] IPV4_ONLY_RESOLVED_RECORD_TYPES =
109             {DnsRecordType.A};
110     private static final InternetProtocolFamily[] IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES =
111             {InternetProtocolFamily.IPv4};
112     private static final DnsRecordType[] IPV4_PREFERRED_RESOLVED_RECORD_TYPES =
113             {DnsRecordType.A, DnsRecordType.AAAA};
114     private static final InternetProtocolFamily[] IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
115             {InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6};
116     private static final DnsRecordType[] IPV6_ONLY_RESOLVED_RECORD_TYPES =
117             {DnsRecordType.AAAA};
118     private static final InternetProtocolFamily[] IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES =
119             {InternetProtocolFamily.IPv6};
120     private static final DnsRecordType[] IPV6_PREFERRED_RESOLVED_RECORD_TYPES =
121             {DnsRecordType.AAAA, DnsRecordType.A};
122     private static final InternetProtocolFamily[] IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
123             {InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4};
124 
125     private static final ChannelHandler NOOP_HANDLER = new ChannelHandlerAdapter() {
126         @Override
127         public boolean isSharable() {
128             return true;
129         }
130     };
131 
132     static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES;
133     static final String[] DEFAULT_SEARCH_DOMAINS;
134     private static final UnixResolverOptions DEFAULT_OPTIONS;
135 
136     static {
137         if (NetUtil.isIpV4StackPreferred() || !anyInterfaceSupportsIpV6()) {
138             DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_ONLY;
139             LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
140         } else {
141             if (NetUtil.isIpV6AddressesPreferred()) {
142                 DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV6_PREFERRED;
143                 LOCALHOST_ADDRESS = NetUtil.LOCALHOST6;
144             } else {
145                 DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_PREFERRED;
146                 LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
147             }
148         }
149         logger.debug("Default ResolvedAddressTypes: {}", DEFAULT_RESOLVE_ADDRESS_TYPES);
150         logger.debug("Localhost address: {}", LOCALHOST_ADDRESS);
151 
152         String hostName;
153         try {
154             hostName = PlatformDependent.isWindows() ? InetAddress.getLocalHost().getHostName() : null;
155         } catch (Exception ignore) {
156             hostName = null;
157         }
158         WINDOWS_HOST_NAME = hostName;
159         logger.debug("Windows hostname: {}", WINDOWS_HOST_NAME);
160 
161         String[] searchDomains;
162         try {
163             List<String> list = PlatformDependent.isWindows()
164                     ? getSearchDomainsHack()
165                     : UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains();
166             searchDomains = list.toArray(EmptyArrays.EMPTY_STRINGS);
167         } catch (Exception ignore) {
168             // Failed to get the system name search domain list.
169             searchDomains = EmptyArrays.EMPTY_STRINGS;
170         }
171         DEFAULT_SEARCH_DOMAINS = searchDomains;
172         logger.debug("Default search domains: {}", Arrays.toString(DEFAULT_SEARCH_DOMAINS));
173 
174         UnixResolverOptions options;
175         try {
176             options = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions();
177         } catch (Exception ignore) {
178             options = UnixResolverOptions.newBuilder().build();
179         }
180         DEFAULT_OPTIONS = options;
181         logger.debug("Default {}", DEFAULT_OPTIONS);
182     }
183 
184     /**
185      * Returns {@code true} if any {@link NetworkInterface} supports {@code IPv6}, {@code false} otherwise.
186      */
187     private static boolean anyInterfaceSupportsIpV6() {
188         for (NetworkInterface iface : NetUtil.NETWORK_INTERFACES) {
189             Enumeration<InetAddress> addresses = iface.getInetAddresses();
190             while (addresses.hasMoreElements()) {
191                 InetAddress inetAddress = addresses.nextElement();
192                 if (inetAddress instanceof Inet6Address && !inetAddress.isAnyLocalAddress() &&
193                         !inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
194                     return true;
195                 }
196             }
197         }
198         return false;
199     }
200 
201     @SuppressWarnings("unchecked")
202     private static List<String> getSearchDomainsHack() throws Exception {
203         // Only try if not using Java9 and later
204         // See https://github.com/netty/netty/issues/9500
205         if (PlatformDependent.javaVersion() < 9) {
206             // This code on Java 9+ yields a warning about illegal reflective access that will be denied in
207             // a future release. There doesn't seem to be a better way to get search domains for Windows yet.
208             Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
209             Method open = configClass.getMethod("open");
210             Method nameservers = configClass.getMethod("searchlist");
211             Object instance = open.invoke(null);
212 
213             return (List<String>) nameservers.invoke(instance);
214         }
215         return Collections.emptyList();
216     }
217 
218     private static final DatagramDnsResponseDecoder DATAGRAM_DECODER = new DatagramDnsResponseDecoder() {
219         @Override
220         protected DnsResponse decodeResponse(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
221             DnsResponse response = super.decodeResponse(ctx, packet);
222             if (packet.content().isReadable()) {
223                 // If there is still something to read we did stop parsing because of a truncated message.
224                 // This can happen if we enabled EDNS0 but our MTU is not big enough to handle all the
225                 // data.
226                 response.setTruncated(true);
227 
228                 if (logger.isDebugEnabled()) {
229                     logger.debug("{} RECEIVED: UDP [{}: {}] truncated packet received, consider adjusting "
230                                     + "maxPayloadSize for the {}.", ctx.channel(), response.id(), packet.sender(),
231                             StringUtil.simpleClassName(DnsNameResolver.class));
232                 }
233             }
234             return response;
235         }
236     };
237     private static final DatagramDnsQueryEncoder DATAGRAM_ENCODER = new DatagramDnsQueryEncoder();
238 
239     // Comparator that ensures we will try first to use the nameservers that use our preferred address type.
240     private final Comparator<InetSocketAddress> nameServerComparator;
241     /**
242      * Manages the {@link DnsQueryContext}s in progress and their query IDs.
243      */
244     private final DnsQueryContextManager queryContextManager = new DnsQueryContextManager();
245 
246     /**
247      * Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}.
248      */
249     private final DnsCache resolveCache;
250     private final AuthoritativeDnsServerCache authoritativeDnsServerCache;
251     private final DnsCnameCache cnameCache;
252     private final DnsServerAddressStream queryDnsServerAddressStream;
253 
254     private final long queryTimeoutMillis;
255     private final int maxQueriesPerResolve;
256     private final ResolvedAddressTypes resolvedAddressTypes;
257     private final InternetProtocolFamily[] resolvedInternetProtocolFamilies;
258     private final boolean recursionDesired;
259     private final int maxPayloadSize;
260     private final boolean optResourceEnabled;
261     private final HostsFileEntriesResolver hostsFileEntriesResolver;
262     private final DnsServerAddressStreamProvider dnsServerAddressStreamProvider;
263     private final String[] searchDomains;
264     private final int ndots;
265     private final boolean supportsAAAARecords;
266     private final boolean supportsARecords;
267     private final InternetProtocolFamily preferredAddressType;
268     private final DnsRecordType[] resolveRecordTypes;
269     private final boolean decodeIdn;
270     private final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory;
271     private final boolean completeOncePreferredResolved;
272     private final DnsResolveChannelProvider resolveChannelProvider;
273     private final Bootstrap socketBootstrap;
274     private final boolean retryWithTcpOnTimeout;
275 
276     private final int maxNumConsolidation;
277     private final Map<String, Future<List<InetAddress>>> inflightLookups;
278 
279     /**
280      * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
281      *
282      * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
283      * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
284      * @param resolveCache the DNS resolved entries cache
285      * @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain
286      * @param dnsQueryLifecycleObserverFactory used to generate new instances of {@link DnsQueryLifecycleObserver} which
287      *                                         can be used to track metrics for DNS servers.
288      * @param queryTimeoutMillis timeout of each DNS query in millis. {@code 0} disables the timeout. If not set or a
289      *                           negative number is set, the default timeout is used.
290      * @param resolvedAddressTypes the preferred address types
291      * @param recursionDesired if recursion desired flag must be set
292      * @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution
293      * @param traceEnabled if trace is enabled
294      * @param maxPayloadSize the capacity of the datagram packet buffer
295      * @param optResourceEnabled if automatic inclusion of a optional records is enabled
296      * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
297      * @param dnsServerAddressStreamProvider The {@link DnsServerAddressStreamProvider} used to determine the name
298      *                                       servers for each hostname lookup.
299      * @param searchDomains the list of search domain
300      *                      (can be null, if so, will try to default to the underlying platform ones)
301      * @param ndots the ndots value
302      * @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received.
303      *                        See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
304      * @deprecated Use {@link DnsNameResolverBuilder}.
305      */
306     @Deprecated
307     public DnsNameResolver(
308             EventLoop eventLoop,
309             ChannelFactory<? extends DatagramChannel> channelFactory,
310             final DnsCache resolveCache,
311             final DnsCache authoritativeDnsServerCache,
312             DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory,
313             long queryTimeoutMillis,
314             ResolvedAddressTypes resolvedAddressTypes,
315             boolean recursionDesired,
316             int maxQueriesPerResolve,
317             boolean traceEnabled,
318             int maxPayloadSize,
319             boolean optResourceEnabled,
320             HostsFileEntriesResolver hostsFileEntriesResolver,
321             DnsServerAddressStreamProvider dnsServerAddressStreamProvider,
322             String[] searchDomains,
323             int ndots,
324             boolean decodeIdn) {
325         this(eventLoop, channelFactory, resolveCache,
326              new AuthoritativeDnsServerCacheAdapter(authoritativeDnsServerCache), dnsQueryLifecycleObserverFactory,
327              queryTimeoutMillis, resolvedAddressTypes, recursionDesired, maxQueriesPerResolve, traceEnabled,
328              maxPayloadSize, optResourceEnabled, hostsFileEntriesResolver, dnsServerAddressStreamProvider,
329              searchDomains, ndots, decodeIdn);
330     }
331 
332     /**
333      * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
334      *
335      * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
336      * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
337      * @param resolveCache the DNS resolved entries cache
338      * @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain
339      * @param dnsQueryLifecycleObserverFactory used to generate new instances of {@link DnsQueryLifecycleObserver} which
340      *                                         can be used to track metrics for DNS servers.
341      * @param queryTimeoutMillis timeout of each DNS query in millis. {@code 0} disables the timeout. If not set or a
342      *                           negative number is set, the default timeout is used.
343      * @param resolvedAddressTypes the preferred address types
344      * @param recursionDesired if recursion desired flag must be set
345      * @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution
346      * @param traceEnabled if trace is enabled
347      * @param maxPayloadSize the capacity of the datagram packet buffer
348      * @param optResourceEnabled if automatic inclusion of a optional records is enabled
349      * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
350      * @param dnsServerAddressStreamProvider The {@link DnsServerAddressStreamProvider} used to determine the name
351      *                                       servers for each hostname lookup.
352      * @param searchDomains the list of search domain
353      *                      (can be null, if so, will try to default to the underlying platform ones)
354      * @param ndots the ndots value
355      * @param decodeIdn {@code true} if domain / host names should be decoded to unicode when received.
356      *                        See <a href="https://tools.ietf.org/html/rfc3492">rfc3492</a>.
357      * @deprecated Use {@link DnsNameResolverBuilder}.
358      */
359     @Deprecated
360     public DnsNameResolver(
361             EventLoop eventLoop,
362             ChannelFactory<? extends DatagramChannel> channelFactory,
363             final DnsCache resolveCache,
364             final AuthoritativeDnsServerCache authoritativeDnsServerCache,
365             DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory,
366             long queryTimeoutMillis,
367             ResolvedAddressTypes resolvedAddressTypes,
368             boolean recursionDesired,
369             int maxQueriesPerResolve,
370             boolean traceEnabled,
371             int maxPayloadSize,
372             boolean optResourceEnabled,
373             HostsFileEntriesResolver hostsFileEntriesResolver,
374             DnsServerAddressStreamProvider dnsServerAddressStreamProvider,
375             String[] searchDomains,
376             int ndots,
377             boolean decodeIdn) {
378         this(eventLoop, channelFactory, null, false, resolveCache,
379                 NoopDnsCnameCache.INSTANCE, authoritativeDnsServerCache, null,
380              dnsQueryLifecycleObserverFactory, queryTimeoutMillis, resolvedAddressTypes, recursionDesired,
381              maxQueriesPerResolve, traceEnabled, maxPayloadSize, optResourceEnabled, hostsFileEntriesResolver,
382              dnsServerAddressStreamProvider, new ThreadLocalNameServerAddressStream(dnsServerAddressStreamProvider),
383              searchDomains, ndots, decodeIdn, false, 0, DnsNameResolverChannelStrategy.ChannelPerResolver);
384     }
385 
386     @SuppressWarnings("deprecation")
387     DnsNameResolver(
388             EventLoop eventLoop,
389             ChannelFactory<? extends DatagramChannel> channelFactory,
390             ChannelFactory<? extends SocketChannel> socketChannelFactory,
391             boolean retryWithTcpOnTimeout,
392             final DnsCache resolveCache,
393             final DnsCnameCache cnameCache,
394             final AuthoritativeDnsServerCache authoritativeDnsServerCache,
395             SocketAddress localAddress,
396             DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory,
397             long queryTimeoutMillis,
398             ResolvedAddressTypes resolvedAddressTypes,
399             boolean recursionDesired,
400             int maxQueriesPerResolve,
401             boolean traceEnabled,
402             final int maxPayloadSize,
403             boolean optResourceEnabled,
404             HostsFileEntriesResolver hostsFileEntriesResolver,
405             DnsServerAddressStreamProvider dnsServerAddressStreamProvider,
406             DnsServerAddressStream queryDnsServerAddressStream,
407             String[] searchDomains,
408             int ndots,
409             boolean decodeIdn,
410             boolean completeOncePreferredResolved,
411             int maxNumConsolidation, DnsNameResolverChannelStrategy datagramChannelStrategy) {
412         super(eventLoop);
413         this.queryTimeoutMillis = queryTimeoutMillis >= 0
414             ? queryTimeoutMillis
415             : TimeUnit.SECONDS.toMillis(DEFAULT_OPTIONS.timeout());
416         this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES;
417         this.recursionDesired = recursionDesired;
418         this.maxQueriesPerResolve = maxQueriesPerResolve > 0 ? maxQueriesPerResolve : DEFAULT_OPTIONS.attempts();
419         this.maxPayloadSize = checkPositive(maxPayloadSize, "maxPayloadSize");
420         this.optResourceEnabled = optResourceEnabled;
421         this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
422         this.dnsServerAddressStreamProvider =
423                 checkNotNull(dnsServerAddressStreamProvider, "dnsServerAddressStreamProvider");
424         this.queryDnsServerAddressStream = checkNotNull(queryDnsServerAddressStream, "queryDnsServerAddressStream");
425         this.resolveCache = checkNotNull(resolveCache, "resolveCache");
426         this.cnameCache = checkNotNull(cnameCache, "cnameCache");
427         this.dnsQueryLifecycleObserverFactory = traceEnabled ?
428                 dnsQueryLifecycleObserverFactory instanceof NoopDnsQueryLifecycleObserverFactory ?
429                         new LoggingDnsQueryLifeCycleObserverFactory() :
430                         new BiDnsQueryLifecycleObserverFactory(new LoggingDnsQueryLifeCycleObserverFactory(),
431                                                                dnsQueryLifecycleObserverFactory) :
432                 checkNotNull(dnsQueryLifecycleObserverFactory, "dnsQueryLifecycleObserverFactory");
433         this.searchDomains = searchDomains != null ? searchDomains.clone() : DEFAULT_SEARCH_DOMAINS;
434         this.ndots = ndots >= 0 ? ndots : DEFAULT_OPTIONS.ndots();
435         this.decodeIdn = decodeIdn;
436         this.completeOncePreferredResolved = completeOncePreferredResolved;
437         this.retryWithTcpOnTimeout = retryWithTcpOnTimeout;
438         if (socketChannelFactory == null) {
439             socketBootstrap = null;
440         } else {
441             socketBootstrap = new Bootstrap();
442             socketBootstrap.option(ChannelOption.SO_REUSEADDR, true)
443                     .group(executor())
444                     .channelFactory(socketChannelFactory)
445                     .attr(DNS_PIPELINE_ATTRIBUTE, Boolean.TRUE)
446                     .handler(NOOP_HANDLER);
447             if (queryTimeoutMillis > 0 && queryTimeoutMillis <= Integer.MAX_VALUE) {
448                 // Set the connect timeout to the same as queryTimeout as otherwise it might take a long
449                 // time for the query to fail in case of a connection timeout.
450                 socketBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) queryTimeoutMillis);
451             }
452         }
453         switch (this.resolvedAddressTypes) {
454             case IPV4_ONLY:
455                 supportsAAAARecords = false;
456                 supportsARecords = true;
457                 resolveRecordTypes = IPV4_ONLY_RESOLVED_RECORD_TYPES;
458                 resolvedInternetProtocolFamilies = IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES;
459                 break;
460             case IPV4_PREFERRED:
461                 supportsAAAARecords = true;
462                 supportsARecords = true;
463                 resolveRecordTypes = IPV4_PREFERRED_RESOLVED_RECORD_TYPES;
464                 resolvedInternetProtocolFamilies = IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES;
465                 break;
466             case IPV6_ONLY:
467                 supportsAAAARecords = true;
468                 supportsARecords = false;
469                 resolveRecordTypes = IPV6_ONLY_RESOLVED_RECORD_TYPES;
470                 resolvedInternetProtocolFamilies = IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES;
471                 break;
472             case IPV6_PREFERRED:
473                 supportsAAAARecords = true;
474                 supportsARecords = true;
475                 resolveRecordTypes = IPV6_PREFERRED_RESOLVED_RECORD_TYPES;
476                 resolvedInternetProtocolFamilies = IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES;
477                 break;
478             default:
479                 throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
480         }
481         preferredAddressType = preferredAddressType(this.resolvedAddressTypes);
482         this.authoritativeDnsServerCache = checkNotNull(authoritativeDnsServerCache, "authoritativeDnsServerCache");
483         nameServerComparator = new NameServerComparator(preferredAddressType.addressType());
484         this.maxNumConsolidation = maxNumConsolidation;
485         if (maxNumConsolidation > 0) {
486             inflightLookups = new HashMap<String, Future<List<InetAddress>>>();
487         } else {
488             inflightLookups = null;
489         }
490 
491         final DnsResponseHandler responseHandler = new DnsResponseHandler(queryContextManager);
492         Bootstrap bootstrap = new Bootstrap()
493                 .channelFactory(channelFactory)
494                 .group(eventLoop)
495                 .attr(DNS_PIPELINE_ATTRIBUTE, Boolean.TRUE)
496                 .handler(new ChannelInitializer<DatagramChannel>() {
497                     @Override
498                     protected void initChannel(DatagramChannel ch) {
499                         ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
500                         ch.pipeline().addLast(DATAGRAM_ENCODER, DATAGRAM_DECODER, responseHandler);
501                     }
502                 });
503         if (localAddress == null) {
504             bootstrap.option(ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION, true);
505         }
506         this.resolveChannelProvider = newProvider(datagramChannelStrategy, bootstrap, localAddress);
507     }
508 
509     private static DnsResolveChannelProvider newProvider(DnsNameResolverChannelStrategy channelStrategy,
510                                                          Bootstrap bootstrap, SocketAddress localAddress) {
511         switch (channelStrategy) {
512             case ChannelPerResolver:
513                 return new DnsResolveChannelPerResolverProvider(bootstrap, localAddress);
514             case ChannelPerResolution:
515                 return new DnsResolveChannelPerResolutionProvider(bootstrap, localAddress);
516             default:
517                 throw new IllegalArgumentException("Unknown DnsNameResolverChannelStrategy: " + channelStrategy);
518         }
519     }
520 
521     static InternetProtocolFamily preferredAddressType(ResolvedAddressTypes resolvedAddressTypes) {
522         switch (resolvedAddressTypes) {
523         case IPV4_ONLY:
524         case IPV4_PREFERRED:
525             return InternetProtocolFamily.IPv4;
526         case IPV6_ONLY:
527         case IPV6_PREFERRED:
528             return InternetProtocolFamily.IPv6;
529         default:
530             throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
531         }
532     }
533 
534     // Only here to override in unit tests.
535     InetSocketAddress newRedirectServerAddress(InetAddress server) {
536         return new InetSocketAddress(server, DNS_PORT);
537     }
538 
539     final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory() {
540         return dnsQueryLifecycleObserverFactory;
541     }
542 
543     /**
544      * Creates a new {@link DnsServerAddressStream} to following a redirected DNS query. By overriding this
545      * it provides the opportunity to sort the name servers before following a redirected DNS query.
546      *
547      * @param hostname the hostname.
548      * @param nameservers The addresses of the DNS servers which are used in the event of a redirect. This may
549      *                    contain resolved and unresolved addresses so the used {@link DnsServerAddressStream} must
550      *                    allow unresolved addresses if you want to include these as well.
551      * @return A {@link DnsServerAddressStream} which will be used to follow the DNS redirect or {@code null} if
552      *         none should be followed.
553      */
554     protected DnsServerAddressStream newRedirectDnsServerStream(
555             @SuppressWarnings("unused") String hostname, List<InetSocketAddress> nameservers) {
556         DnsServerAddressStream cached = authoritativeDnsServerCache().get(hostname);
557         if (cached == null || cached.size() == 0) {
558             // If there is no cache hit (which may be the case for example when a NoopAuthoritativeDnsServerCache
559             // is used), we will just directly use the provided nameservers.
560             Collections.sort(nameservers, nameServerComparator);
561             return new SequentialDnsServerAddressStream(nameservers, 0);
562         }
563         return cached;
564     }
565 
566     /**
567      * Returns the resolution cache.
568      */
569     public DnsCache resolveCache() {
570         return resolveCache;
571     }
572 
573     /**
574      * Returns the {@link DnsCnameCache}.
575      */
576     public DnsCnameCache cnameCache() {
577         return cnameCache;
578     }
579 
580     /**
581      * Returns the cache used for authoritative DNS servers for a domain.
582      */
583     public AuthoritativeDnsServerCache authoritativeDnsServerCache() {
584         return authoritativeDnsServerCache;
585     }
586 
587     /**
588      * Returns the timeout of each DNS query performed by this resolver (in milliseconds).
589      * The default value is 5 seconds.
590      */
591     public long queryTimeoutMillis() {
592         return queryTimeoutMillis;
593     }
594 
595     /**
596      * Returns the dns server address stream used for DNS queries (not resolve).
597      */
598     public DnsServerAddressStream queryDnsServerAddressStream() {
599         return queryDnsServerAddressStream;
600     }
601 
602     /**
603      * Returns the {@link ResolvedAddressTypes} resolved by {@link #resolve(String)}.
604      * The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}.
605      */
606     public ResolvedAddressTypes resolvedAddressTypes() {
607         return resolvedAddressTypes;
608     }
609 
610     InternetProtocolFamily[] resolvedInternetProtocolFamiliesUnsafe() {
611         return resolvedInternetProtocolFamilies;
612     }
613 
614     final String[] searchDomains() {
615         return searchDomains;
616     }
617 
618     final int ndots() {
619         return ndots;
620     }
621 
622     final boolean supportsAAAARecords() {
623         return supportsAAAARecords;
624     }
625 
626     final boolean supportsARecords() {
627         return supportsARecords;
628     }
629 
630     final InternetProtocolFamily preferredAddressType() {
631         return preferredAddressType;
632     }
633 
634     final DnsRecordType[] resolveRecordTypes() {
635         return resolveRecordTypes;
636     }
637 
638     final boolean isDecodeIdn() {
639         return decodeIdn;
640     }
641 
642     /**
643      * Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
644      * The default value is {@code true}.
645      */
646     public boolean isRecursionDesired() {
647         return recursionDesired;
648     }
649 
650     /**
651      * Returns the maximum allowed number of DNS queries to send when resolving a host name.
652      * The default value is {@code 8}.
653      */
654     public int maxQueriesPerResolve() {
655         return maxQueriesPerResolve;
656     }
657 
658     /**
659      * Returns the capacity of the datagram packet buffer (in bytes).  The default value is {@code 4096} bytes.
660      */
661     public int maxPayloadSize() {
662         return maxPayloadSize;
663     }
664 
665     /**
666      * Returns the automatic inclusion of a optional records that tries to give the remote DNS server a hint about how
667      * much data the resolver can read per response is enabled.
668      */
669     public boolean isOptResourceEnabled() {
670         return optResourceEnabled;
671     }
672 
673     /**
674      * Returns the component that tries to resolve hostnames against the hosts file prior to asking to
675      * remotes DNS servers.
676      */
677     public HostsFileEntriesResolver hostsFileEntriesResolver() {
678         return hostsFileEntriesResolver;
679     }
680 
681     /**
682      * Closes the internal datagram channel used for sending and receiving DNS messages, and clears all DNS resource
683      * records from the cache. Attempting to send a DNS query or to resolve a domain name will fail once this method
684      * has been called.
685      */
686     @Override
687     public void close() {
688         resolveChannelProvider.close();
689         resolveCache.clear();
690         cnameCache.clear();
691         authoritativeDnsServerCache.clear();
692     }
693 
694     @Override
695     protected EventLoop executor() {
696         return (EventLoop) super.executor();
697     }
698 
699     private InetAddress resolveHostsFileEntry(String hostname) {
700         if (hostsFileEntriesResolver == null) {
701             return null;
702         }
703         InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes);
704         return address == null && isLocalWindowsHost(hostname) ? LOCALHOST_ADDRESS : address;
705     }
706 
707     private List<InetAddress> resolveHostsFileEntries(String hostname) {
708         if (hostsFileEntriesResolver == null) {
709             return null;
710         }
711         List<InetAddress> addresses;
712         if (hostsFileEntriesResolver instanceof DefaultHostsFileEntriesResolver) {
713             addresses = ((DefaultHostsFileEntriesResolver) hostsFileEntriesResolver)
714                     .addresses(hostname, resolvedAddressTypes);
715         } else {
716             InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes);
717             addresses = address != null ? Collections.singletonList(address) : null;
718         }
719         return addresses == null && isLocalWindowsHost(hostname) ?
720                 Collections.singletonList(LOCALHOST_ADDRESS) : addresses;
721     }
722 
723     /**
724      * Checks whether the given hostname is the localhost/host (computer) name on Windows OS.
725      * Windows OS removed the localhost/host (computer) name information from the hosts file in the later versions
726      * and such hostname cannot be resolved from hosts file.
727      * See https://github.com/netty/netty/issues/5386
728      * See https://github.com/netty/netty/issues/11142
729      */
730     private static boolean isLocalWindowsHost(String hostname) {
731         return PlatformDependent.isWindows() &&
732                 (LOCALHOST.equalsIgnoreCase(hostname) ||
733                         (WINDOWS_HOST_NAME != null && WINDOWS_HOST_NAME.equalsIgnoreCase(hostname)));
734     }
735 
736     /**
737      * Resolves the specified name into an address.
738      *
739      * @param inetHost the name to resolve
740      * @param additionals additional records ({@code OPT})
741      *
742      * @return the address as the result of the resolution
743      */
744     public final Future<InetAddress> resolve(String inetHost, Iterable<DnsRecord> additionals) {
745         return resolve(inetHost, additionals, executor().<InetAddress>newPromise());
746     }
747 
748     /**
749      * Resolves the specified name into an address.
750      *
751      * @param inetHost the name to resolve
752      * @param additionals additional records ({@code OPT})
753      * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
754      *
755      * @return the address as the result of the resolution
756      */
757     public final Future<InetAddress> resolve(String inetHost, Iterable<DnsRecord> additionals,
758                                              Promise<InetAddress> promise) {
759         checkNotNull(promise, "promise");
760         DnsRecord[] additionalsArray = toArray(additionals, true);
761         try {
762             doResolve(inetHost, additionalsArray, promise, resolveCache);
763             return promise;
764         } catch (Exception e) {
765             return promise.setFailure(e);
766         }
767     }
768 
769     /**
770      * Resolves the specified host name and port into a list of address.
771      *
772      * @param inetHost the name to resolve
773      * @param additionals additional records ({@code OPT})
774      *
775      * @return the list of the address as the result of the resolution
776      */
777     public final Future<List<InetAddress>> resolveAll(String inetHost, Iterable<DnsRecord> additionals) {
778         return resolveAll(inetHost, additionals, executor().<List<InetAddress>>newPromise());
779     }
780 
781     /**
782      * Resolves the specified host name and port into a list of address.
783      *
784      * @param inetHost the name to resolve
785      * @param additionals additional records ({@code OPT})
786      * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
787      *
788      * @return the list of the address as the result of the resolution
789      */
790     public final Future<List<InetAddress>> resolveAll(String inetHost, Iterable<DnsRecord> additionals,
791                                                       Promise<List<InetAddress>> promise) {
792         checkNotNull(promise, "promise");
793         DnsRecord[] additionalsArray = toArray(additionals, true);
794         try {
795             doResolveAll(inetHost, additionalsArray, promise, resolveCache);
796             return promise;
797         } catch (Exception e) {
798             return promise.setFailure(e);
799         }
800     }
801 
802     @Override
803     protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
804         doResolve(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
805     }
806 
807     /**
808      * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike
809      * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers.
810      * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured
811      * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the
812      * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned.
813      *
814      * @param question the question
815      *
816      * @return the list of the {@link DnsRecord}s as the result of the resolution
817      */
818     public final Future<List<DnsRecord>> resolveAll(DnsQuestion question) {
819         return resolveAll(question, EMPTY_ADDITIONALS, executor().<List<DnsRecord>>newPromise());
820     }
821 
822     /**
823      * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike
824      * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers.
825      * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured
826      * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the
827      * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned.
828      *
829      * @param question the question
830      * @param additionals additional records ({@code OPT})
831      *
832      * @return the list of the {@link DnsRecord}s as the result of the resolution
833      */
834     public final Future<List<DnsRecord>> resolveAll(DnsQuestion question, Iterable<DnsRecord> additionals) {
835         return resolveAll(question, additionals, executor().<List<DnsRecord>>newPromise());
836     }
837 
838     /**
839      * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike
840      * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers.
841      * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured
842      * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the
843      * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned.
844      *
845      * @param question the question
846      * @param additionals additional records ({@code OPT})
847      * @param promise the {@link Promise} which will be fulfilled when the resolution is finished
848      *
849      * @return the list of the {@link DnsRecord}s as the result of the resolution
850      */
851     public final Future<List<DnsRecord>> resolveAll(DnsQuestion question, Iterable<DnsRecord> additionals,
852                                                     Promise<List<DnsRecord>> promise) {
853         final DnsRecord[] additionalsArray = toArray(additionals, true);
854         return resolveAll(question, additionalsArray, promise);
855     }
856 
857     private Future<List<DnsRecord>> resolveAll(final DnsQuestion question, final DnsRecord[] additionals,
858                                                final Promise<List<DnsRecord>> promise) {
859         checkNotNull(question, "question");
860         checkNotNull(promise, "promise");
861 
862         // Respect /etc/hosts as well if the record type is A or AAAA.
863         final DnsRecordType type = question.type();
864         final String hostname = question.name();
865 
866         if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
867             final List<InetAddress> hostsFileEntries = resolveHostsFileEntries(hostname);
868             if (hostsFileEntries != null) {
869                 List<DnsRecord> result = new ArrayList<DnsRecord>();
870                 for (InetAddress hostsFileEntry : hostsFileEntries) {
871                     ByteBuf content = null;
872                     if (hostsFileEntry instanceof Inet4Address) {
873                         if (type == DnsRecordType.A) {
874                             content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
875                         }
876                     } else if (hostsFileEntry instanceof Inet6Address) {
877                         if (type == DnsRecordType.AAAA) {
878                             content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
879                         }
880                     }
881                     if (content != null) {
882                         // Our current implementation does not support reloading the hosts file,
883                         // so use a fairly large TTL (1 day, i.e. 86400 seconds).
884                         result.add(new DefaultDnsRawRecord(hostname, type, 86400, content));
885                     }
886                 }
887 
888                 if (!result.isEmpty()) {
889                     if (!trySuccess(promise, result)) {
890                         // We were not able to transfer ownership, release the records to prevent leaks.
891                         for (DnsRecord r: result) {
892                             ReferenceCountUtil.safeRelease(r);
893                         }
894                     }
895                     return promise;
896                 }
897             }
898         }
899 
900         ChannelFuture f = resolveChannelProvider.nextResolveChannel(promise);
901         if (f.isDone()) {
902             resolveAllNow(f, hostname, question, additionals, promise);
903         } else {
904             f.addListener(new ChannelFutureListener() {
905                 @Override
906                 public void operationComplete(ChannelFuture f) {
907                     resolveAllNow(f, hostname, question, additionals, promise);
908                 }
909             });
910         }
911         return promise;
912     }
913 
914     private void resolveAllNow(ChannelFuture f, String hostname, final DnsQuestion question,
915                                final DnsRecord[] additionals, final Promise<List<DnsRecord>> promise) {
916         if (f.isSuccess()) {
917             // It was not A/AAAA question or there was no entry in /etc/hosts.
918             final DnsServerAddressStream nameServerAddrs =
919                     dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
920 
921             new DnsRecordResolveContext(DnsNameResolver.this, f.channel(), promise, question, additionals,
922                     nameServerAddrs, maxQueriesPerResolve).resolve(promise);
923         } else {
924             UnknownHostException e = toException(f, hostname, question, additionals);
925             promise.setFailure(e);
926         }
927     }
928 
929     private static UnknownHostException toException(
930             ChannelFuture f, String hostname, DnsQuestion question, DnsRecord[] additionals) {
931         UnknownHostException e = new UnknownHostException(
932                 "Failed to resolve '" + hostname + "', couldn't setup transport: " + f.channel());
933         e.initCause(f.cause());
934 
935         if (question != null) {
936             ReferenceCountUtil.release(question);
937         }
938         for (DnsRecord record : additionals) {
939             ReferenceCountUtil.release(record);
940         }
941         return e;
942     }
943 
944     private static DnsRecord[] toArray(Iterable<DnsRecord> additionals, boolean validateType) {
945         checkNotNull(additionals, "additionals");
946         if (additionals instanceof Collection) {
947             Collection<DnsRecord> records = (Collection<DnsRecord>) additionals;
948             for (DnsRecord r: additionals) {
949                 validateAdditional(r, validateType);
950             }
951             return records.toArray(new DnsRecord[records.size()]);
952         }
953 
954         Iterator<DnsRecord> additionalsIt = additionals.iterator();
955         if (!additionalsIt.hasNext()) {
956             return EMPTY_ADDITIONALS;
957         }
958         List<DnsRecord> records = new ArrayList<DnsRecord>();
959         do {
960             DnsRecord r = additionalsIt.next();
961             validateAdditional(r, validateType);
962             records.add(r);
963         } while (additionalsIt.hasNext());
964 
965         return records.toArray(new DnsRecord[records.size()]);
966     }
967 
968     private static void validateAdditional(DnsRecord record, boolean validateType) {
969         checkNotNull(record, "record");
970         if (validateType && record instanceof DnsRawRecord) {
971             throw new IllegalArgumentException("DnsRawRecord implementations not allowed: " + record);
972         }
973     }
974 
975     private InetAddress loopbackAddress() {
976         return preferredAddressType().localhost();
977     }
978 
979     /**
980      * Hook designed for extensibility so one can pass a different cache on each resolution attempt
981      * instead of using the global one.
982      */
983     protected void doResolve(String inetHost,
984                              final DnsRecord[] additionals,
985                              final Promise<InetAddress> promise,
986                              final DnsCache resolveCache) throws Exception {
987         if (inetHost == null || inetHost.isEmpty()) {
988             // If an empty hostname is used we should use "localhost", just like InetAddress.getByName(...) does.
989             promise.setSuccess(loopbackAddress());
990             return;
991         }
992         final InetAddress address = NetUtil.createInetAddressFromIpAddressString(inetHost);
993         if (address != null) {
994             // The inetHost is actually an ipaddress.
995             promise.setSuccess(address);
996             return;
997         }
998 
999         final String hostname = hostname(inetHost);
1000 
1001         InetAddress hostsFileEntry = resolveHostsFileEntry(hostname);
1002         if (hostsFileEntry != null) {
1003             promise.setSuccess(hostsFileEntry);
1004             return;
1005         }
1006 
1007         if (!doResolveCached(hostname, additionals, promise, resolveCache)) {
1008             ChannelFuture f = resolveChannelProvider.nextResolveChannel(promise);
1009             if (f.isDone()) {
1010                 doResolveNow(f, hostname, additionals, promise, resolveCache);
1011             } else {
1012                 f.addListener(new ChannelFutureListener() {
1013                     @Override
1014                     public void operationComplete(ChannelFuture f) {
1015                         doResolveNow(f, hostname, additionals, promise, resolveCache);
1016                     }
1017                 });
1018             }
1019         }
1020     }
1021 
1022     private void doResolveNow(ChannelFuture f, final String hostname, final DnsRecord[] additionals,
1023                               final Promise<InetAddress> promise,
1024                               final DnsCache resolveCache) {
1025         if (f.isSuccess()) {
1026             doResolveUncached(f.channel(), hostname, additionals, promise,
1027                     resolveCache, completeOncePreferredResolved);
1028         } else {
1029             UnknownHostException e = toException(f, hostname, null, additionals);
1030             promise.setFailure(e);
1031         }
1032     }
1033 
1034     private boolean doResolveCached(String hostname,
1035                                     DnsRecord[] additionals,
1036                                     Promise<InetAddress> promise,
1037                                     DnsCache resolveCache) {
1038         final List<? extends DnsCacheEntry> cachedEntries = resolveCache.get(hostname, additionals);
1039         if (cachedEntries == null || cachedEntries.isEmpty()) {
1040             return false;
1041         }
1042 
1043         Throwable cause = cachedEntries.get(0).cause();
1044         if (cause == null) {
1045             final int numEntries = cachedEntries.size();
1046             // Find the first entry with the preferred address type.
1047             for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) {
1048                 for (int i = 0; i < numEntries; i++) {
1049                     final DnsCacheEntry e = cachedEntries.get(i);
1050                     if (f.addressType().isInstance(e.address())) {
1051                         trySuccess(promise, e.address());
1052                         return true;
1053                     }
1054                 }
1055             }
1056             return false;
1057         } else {
1058             tryFailure(promise, cause);
1059             return true;
1060         }
1061     }
1062 
1063     static <T> boolean trySuccess(Promise<T> promise, T result) {
1064         final boolean notifiedRecords = promise.trySuccess(result);
1065         if (!notifiedRecords) {
1066             // There is nothing really wrong with not be able to notify the promise as we may have raced here because
1067             // of multiple queries that have been executed. Log it with trace level anyway just in case the user
1068             // wants to better understand what happened.
1069             logger.trace("Failed to notify success ({}) to a promise: {}", result, promise);
1070         }
1071         return notifiedRecords;
1072     }
1073 
1074     private static void tryFailure(Promise<?> promise, Throwable cause) {
1075         if (!promise.tryFailure(cause)) {
1076             // There is nothing really wrong with not be able to notify the promise as we may have raced here because
1077             // of multiple queries that have been executed. Log it with trace level anyway just in case the user
1078             // wants to better understand what happened.
1079             logger.trace("Failed to notify failure to a promise: {}", promise, cause);
1080         }
1081     }
1082 
1083     private void doResolveUncached(Channel channel,
1084                                    String hostname,
1085                                    DnsRecord[] additionals,
1086                                    final Promise<InetAddress> promise,
1087                                    DnsCache resolveCache, boolean completeEarlyIfPossible) {
1088         final Promise<List<InetAddress>> allPromise = executor().newPromise();
1089         doResolveAllUncached(channel, hostname, additionals, promise, allPromise,
1090                 resolveCache, completeEarlyIfPossible);
1091         allPromise.addListener(new FutureListener<List<InetAddress>>() {
1092             @Override
1093             public void operationComplete(Future<List<InetAddress>> future) {
1094                 if (future.isSuccess()) {
1095                     trySuccess(promise, future.getNow().get(0));
1096                 } else {
1097                     tryFailure(promise, future.cause());
1098                 }
1099             }
1100         });
1101     }
1102 
1103     @Override
1104     protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise) throws Exception {
1105         doResolveAll(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
1106     }
1107 
1108     /**
1109      * Hook designed for extensibility so one can pass a different cache on each resolution attempt
1110      * instead of using the global one.
1111      */
1112     protected void doResolveAll(String inetHost,
1113                                 final DnsRecord[] additionals,
1114                                 final Promise<List<InetAddress>> promise,
1115                                 final DnsCache resolveCache) throws Exception {
1116         if (inetHost == null || inetHost.isEmpty()) {
1117             // If an empty hostname is used we should use "localhost", just like InetAddress.getAllByName(...) does.
1118             promise.setSuccess(Collections.singletonList(loopbackAddress()));
1119             return;
1120         }
1121         final InetAddress address = NetUtil.createInetAddressFromIpAddressString(inetHost);
1122         if (address != null) {
1123             // The unresolvedAddress was created via a String that contains an ipaddress.
1124             promise.setSuccess(Collections.singletonList(address));
1125             return;
1126         }
1127 
1128         final String hostname = hostname(inetHost);
1129 
1130         List<InetAddress> hostsFileEntries = resolveHostsFileEntries(hostname);
1131         if (hostsFileEntries != null) {
1132             promise.setSuccess(hostsFileEntries);
1133             return;
1134         }
1135 
1136         if (!doResolveAllCached(hostname, additionals, promise, resolveCache, this.searchDomains(),
1137                 ndots(), resolvedInternetProtocolFamilies)) {
1138             ChannelFuture f = resolveChannelProvider.nextResolveChannel(promise);
1139             if (f.isDone()) {
1140                 doResolveAllNow(f, hostname, additionals, promise, resolveCache);
1141             } else {
1142                 f.addListener(new ChannelFutureListener() {
1143                     @Override
1144                     public void operationComplete(ChannelFuture f) {
1145                         doResolveAllNow(f, hostname, additionals, promise, resolveCache);
1146                     }
1147                 });
1148             }
1149         }
1150     }
1151 
1152     private void doResolveAllNow(ChannelFuture f, final String hostname, final DnsRecord[] additionals,
1153                               final Promise<List<InetAddress>> promise,
1154                               final DnsCache resolveCache) {
1155         if (f.isSuccess()) {
1156             doResolveAllUncached(f.channel(), hostname, additionals, promise, promise,
1157                     resolveCache, completeOncePreferredResolved);
1158         } else {
1159             UnknownHostException e = toException(f, hostname, null, additionals);
1160             promise.setFailure(e);
1161         }
1162     }
1163 
1164     private static boolean hasEntries(List<? extends DnsCacheEntry> cachedEntries) {
1165         return cachedEntries != null && !cachedEntries.isEmpty();
1166     }
1167 
1168     static boolean doResolveAllCached(String hostname,
1169                                       DnsRecord[] additionals,
1170                                       Promise<List<InetAddress>> promise,
1171                                       DnsCache resolveCache,
1172                                       String[] searchDomains,
1173                                       int ndots,
1174                                       InternetProtocolFamily[] resolvedInternetProtocolFamilies) {
1175         List<? extends DnsCacheEntry> cachedEntries = resolveCache.get(hostname, additionals);
1176         if (!hasEntries(cachedEntries) && searchDomains != null && ndots != 0
1177                 && !StringUtil.endsWith(hostname, '.')) {
1178             for (String searchDomain : searchDomains) {
1179                 final String initialHostname = hostname + '.' + searchDomain;
1180                 cachedEntries = resolveCache.get(initialHostname, additionals);
1181                 if (hasEntries(cachedEntries)) {
1182                     break;
1183                 }
1184             }
1185         }
1186         if (!hasEntries(cachedEntries)) {
1187             return false;
1188         }
1189 
1190         Throwable cause = cachedEntries.get(0).cause();
1191         if (cause == null) {
1192             List<InetAddress> result = null;
1193             final int numEntries = cachedEntries.size();
1194             for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) {
1195                 for (int i = 0; i < numEntries; i++) {
1196                     final DnsCacheEntry e = cachedEntries.get(i);
1197                     if (f.addressType().isInstance(e.address())) {
1198                         if (result == null) {
1199                             result = new ArrayList<InetAddress>(numEntries);
1200                         }
1201                         result.add(e.address());
1202                     }
1203                 }
1204             }
1205             if (result != null) {
1206                 trySuccess(promise, result);
1207                 return true;
1208             }
1209             return false;
1210         } else {
1211             tryFailure(promise, cause);
1212             return true;
1213         }
1214     }
1215 
1216     private void doResolveAllUncached(final Channel channel,
1217                                       final String hostname,
1218                                       final DnsRecord[] additionals,
1219                                       final Promise<?> originalPromise,
1220                                       final Promise<List<InetAddress>> promise,
1221                                       final DnsCache resolveCache,
1222                                       final boolean completeEarlyIfPossible) {
1223         // Call doResolveUncached0(...) in the EventLoop as we may need to submit multiple queries which would need
1224         // to submit multiple Runnable at the end if we are not already on the EventLoop.
1225         EventExecutor executor = executor();
1226         if (executor.inEventLoop()) {
1227             doResolveAllUncached0(channel, hostname, additionals, originalPromise,
1228                                   promise, resolveCache, completeEarlyIfPossible);
1229         } else {
1230             executor.execute(new Runnable() {
1231                 @Override
1232                 public void run() {
1233                     doResolveAllUncached0(channel, hostname, additionals, originalPromise,
1234                                           promise, resolveCache, completeEarlyIfPossible);
1235                 }
1236             });
1237         }
1238     }
1239 
1240     private void doResolveAllUncached0(final Channel channel,
1241                                        final String hostname,
1242                                        final DnsRecord[] additionals,
1243                                        final Promise<?> originalPromise,
1244                                        final Promise<List<InetAddress>> promise,
1245                                        final DnsCache resolveCache,
1246                                        final boolean completeEarlyIfPossible) {
1247 
1248         assert executor().inEventLoop();
1249 
1250         if (inflightLookups != null && (additionals == null || additionals.length == 0)) {
1251             Future<List<InetAddress>> inflightFuture = inflightLookups.get(hostname);
1252             if (inflightFuture != null) {
1253                 inflightFuture.addListener(new GenericFutureListener<Future<? super List<InetAddress>>>() {
1254                     @SuppressWarnings("unchecked")
1255                     @Override
1256                     public void operationComplete(Future<? super List<InetAddress>> future) {
1257                         if (future.isSuccess()) {
1258                             promise.setSuccess((List<InetAddress>) future.getNow());
1259                         } else {
1260                             Throwable cause = future.cause();
1261                             if (isTimeoutError(cause)) {
1262                                 // The failure was caused by a timeout. This might be happening as a result of
1263                                 // the remote server be overloaded for some short amount of time or because
1264                                 // UDP packets were dropped on the floor. In this case lets try to just do the
1265                                 // query explicit and don't cascade this possible temporary failure.
1266                                 resolveNow(channel, hostname, additionals, originalPromise, promise,
1267                                         resolveCache, completeEarlyIfPossible);
1268                             } else {
1269                                 promise.setFailure(cause);
1270                             }
1271                         }
1272                     }
1273                 });
1274                 return;
1275             // Check if we have space left in the map.
1276             } else if (inflightLookups.size() < maxNumConsolidation) {
1277                 inflightLookups.put(hostname, promise);
1278                 promise.addListener(new GenericFutureListener<Future<? super List<InetAddress>>>() {
1279                     @Override
1280                     public void operationComplete(Future<? super List<InetAddress>> future) {
1281                         inflightLookups.remove(hostname);
1282                     }
1283                 });
1284             }
1285         }
1286         resolveNow(channel, hostname, additionals, originalPromise, promise,
1287                 resolveCache, completeEarlyIfPossible);
1288     }
1289 
1290     private void resolveNow(final Channel channel,
1291                             final String hostname,
1292                             final DnsRecord[] additionals,
1293                             final Promise<?> originalPromise,
1294                             final Promise<List<InetAddress>> promise,
1295                             final DnsCache resolveCache,
1296                             final boolean completeEarlyIfPossible) {
1297         final DnsServerAddressStream nameServerAddrs =
1298                 dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
1299         DnsAddressResolveContext ctx = new DnsAddressResolveContext(this, channel,
1300                 originalPromise, hostname, additionals, nameServerAddrs, maxQueriesPerResolve, resolveCache,
1301                 authoritativeDnsServerCache, completeEarlyIfPossible);
1302         ctx.resolve(promise);
1303     }
1304 
1305     private static String hostname(String inetHost) {
1306         String hostname = IDN.toASCII(inetHost);
1307         // Check for https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6894622
1308         if (StringUtil.endsWith(inetHost, '.') && !StringUtil.endsWith(hostname, '.')) {
1309             hostname += ".";
1310         }
1311         return hostname;
1312     }
1313 
1314     /**
1315      * Sends a DNS query with the specified question.
1316      */
1317     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(DnsQuestion question) {
1318         return query(nextNameServerAddress(), question);
1319     }
1320 
1321     /**
1322      * Sends a DNS query with the specified question with additional records.
1323      */
1324     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1325             DnsQuestion question, Iterable<DnsRecord> additionals) {
1326         return query(nextNameServerAddress(), question, additionals);
1327     }
1328 
1329     /**
1330      * Sends a DNS query with the specified question.
1331      */
1332     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1333             DnsQuestion question, Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1334         return query(nextNameServerAddress(), question, Collections.<DnsRecord>emptyList(), promise);
1335     }
1336 
1337     private InetSocketAddress nextNameServerAddress() {
1338         return queryDnsServerAddressStream.next();
1339     }
1340 
1341     /**
1342      * Sends a DNS query with the specified question using the specified name server list.
1343      */
1344     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1345             final InetSocketAddress nameServerAddr, final DnsQuestion question) {
1346         return query(nameServerAddr, question, Collections.<DnsRecord>emptyList());
1347     }
1348 
1349     /**
1350      * Sends a DNS query with the specified question with additional records using the specified name server list.
1351      */
1352     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1353             final InetSocketAddress nameServerAddr, final DnsQuestion question, final Iterable<DnsRecord> additionals) {
1354         return query(nameServerAddr, question, additionals,
1355                 executor().<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>>newPromise());
1356     }
1357 
1358     /**
1359      * Sends a DNS query with the specified question using the specified name server list.
1360      */
1361     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1362             final InetSocketAddress nameServerAddr, final DnsQuestion question,
1363             final Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1364         return query(nameServerAddr, question, Collections.<DnsRecord>emptyList(), promise);
1365     }
1366 
1367     /**
1368      * Sends a DNS query with the specified question with additional records using the specified name server list.
1369      */
1370     public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1371             final InetSocketAddress nameServerAddr, final DnsQuestion question,
1372             final Iterable<DnsRecord> additionals,
1373             final Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1374 
1375         ChannelFuture f = resolveChannelProvider.nextResolveChannel(promise);
1376         final DnsRecord[] additionalsArray = toArray(additionals, false);
1377         if (f.isDone()) {
1378             if (f.isSuccess()) {
1379                 return doQuery(f.channel(), nameServerAddr, question,
1380                         NoopDnsQueryLifecycleObserver.INSTANCE, additionalsArray,
1381                         true, promise);
1382             } else {
1383                 UnknownHostException e = toException(f, question.name(), question, additionalsArray);
1384                 promise.setFailure(e);
1385                 return executor().newFailedFuture(e);
1386             }
1387         } else {
1388             final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> p = executor().newPromise();
1389             f.addListener(new ChannelFutureListener() {
1390                 @Override
1391                 public void operationComplete(ChannelFuture f) {
1392                     if (f.isSuccess()) {
1393                         Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> qf = doQuery(
1394                                 f.channel(), nameServerAddr, question, NoopDnsQueryLifecycleObserver.INSTANCE,
1395                                 additionalsArray, true, promise);
1396                         PromiseNotifier.cascade(qf, p);
1397                     } else {
1398                         UnknownHostException e = toException(f, question.name(), question, additionalsArray);
1399                         promise.setFailure(e);
1400                         p.setFailure(e);
1401                     }
1402                 }
1403             });
1404             return p;
1405         }
1406     }
1407 
1408     /**
1409      * Returns {@code true} if the {@link Throwable} was caused by an timeout or transport error.
1410      * These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this
1411      * {@link DnsNameResolver}.
1412      */
1413     public static boolean isTransportOrTimeoutError(Throwable cause) {
1414         return cause != null && cause.getCause() instanceof DnsNameResolverException;
1415     }
1416 
1417     /**
1418      * Returns {@code true} if the {@link Throwable} was caused by an timeout.
1419      * These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this
1420      * {@link DnsNameResolver}.
1421      */
1422     public static boolean isTimeoutError(Throwable cause) {
1423         return cause != null && cause.getCause() instanceof DnsNameResolverTimeoutException;
1424     }
1425 
1426     final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> doQuery(
1427             Channel channel,
1428             InetSocketAddress nameServerAddr, DnsQuestion question,
1429             final DnsQueryLifecycleObserver queryLifecycleObserver,
1430             DnsRecord[] additionals, boolean flush,
1431             Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1432 
1433         final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> castPromise = cast(
1434                 checkNotNull(promise, "promise"));
1435         final int payloadSize = isOptResourceEnabled() ? maxPayloadSize() : 0;
1436         try {
1437             DnsQueryContext queryContext = new DatagramDnsQueryContext(channel, nameServerAddr,
1438                     queryContextManager, payloadSize, isRecursionDesired(), queryTimeoutMillis(), question, additionals,
1439                     castPromise, socketBootstrap, retryWithTcpOnTimeout);
1440             ChannelFuture future = queryContext.writeQuery(flush);
1441             queryLifecycleObserver.queryWritten(nameServerAddr, future);
1442             return castPromise;
1443         } catch (Exception e) {
1444             return castPromise.setFailure(e);
1445         }
1446     }
1447 
1448     @SuppressWarnings("unchecked")
1449     private static Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> cast(Promise<?> promise) {
1450         return (Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>>) promise;
1451     }
1452 
1453     final DnsServerAddressStream newNameServerAddressStream(String hostname) {
1454         return dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
1455     }
1456 
1457     private static final class DnsResponseHandler extends ChannelInboundHandlerAdapter {
1458 
1459         private final DnsQueryContextManager queryContextManager;
1460 
1461         DnsResponseHandler(DnsQueryContextManager queryContextManager) {
1462             this.queryContextManager = queryContextManager;
1463         }
1464 
1465         @Override
1466         public boolean isSharable() {
1467             return true;
1468         }
1469 
1470         @Override
1471         public void channelRead(ChannelHandlerContext ctx, Object msg) {
1472             final Channel qCh = ctx.channel();
1473             final DatagramDnsResponse res = (DatagramDnsResponse) msg;
1474             final int queryId = res.id();
1475             logger.debug("{} RECEIVED: UDP [{}: {}], {}", qCh, queryId, res.sender(), res);
1476 
1477             final DnsQueryContext qCtx = queryContextManager.get(res.sender(), queryId);
1478             if (qCtx == null) {
1479                 logger.debug("{} Received a DNS response with an unknown ID: UDP [{}: {}]",
1480                         qCh, queryId, res.sender());
1481                 res.release();
1482                 return;
1483             } else if (qCtx.isDone()) {
1484                 logger.debug("{} Received a DNS response for a query that was timed out or cancelled: UDP [{}: {}]",
1485                         qCh, queryId, res.sender());
1486                 res.release();
1487                 return;
1488             }
1489 
1490             // The context will handle truncation itself.
1491             qCtx.finishSuccess(res, res.isTruncated());
1492         }
1493 
1494         @Override
1495         public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
1496             if (cause instanceof CorruptedFrameException) {
1497                 logger.debug("{} Unable to decode DNS response: UDP", ctx.channel(), cause);
1498             } else {
1499                 logger.warn("{} Unexpected exception: UDP", ctx.channel(), cause);
1500             }
1501         }
1502     }
1503 
1504     private interface DnsResolveChannelProvider {
1505 
1506         /**
1507          * Return the next {@link ChannelFuture} that contains the {@link Channel} that should be used for resolving
1508          * a chain of queries.
1509          *
1510          * @param resolutionFuture  the {@link Future} that will be notified once th resolution completes.
1511          * @return                  the {@link ChannelFuture}
1512          */
1513         <T> ChannelFuture nextResolveChannel(Future<T> resolutionFuture);
1514 
1515         /**
1516          * Close the {@link DnsResolveChannelProvider} and so cleanup resources if needed.
1517          */
1518         void close();
1519     }
1520 
1521     private static ChannelFuture registerOrBind(Bootstrap bootstrap, SocketAddress localAddress) {
1522         return localAddress == null ? bootstrap.register() : bootstrap.bind(localAddress);
1523     }
1524 
1525     private static final class DnsResolveChannelPerResolverProvider implements DnsResolveChannelProvider {
1526 
1527         private final ChannelFuture resolveChannelFuture;
1528 
1529         DnsResolveChannelPerResolverProvider(Bootstrap bootstrap, SocketAddress localAddress) {
1530             resolveChannelFuture = registerOrBind(bootstrap, localAddress);
1531         }
1532 
1533         @Override
1534         public <T> ChannelFuture nextResolveChannel(Future<T> resolutionFuture) {
1535             return resolveChannelFuture;
1536         }
1537 
1538         @Override
1539         public void close() {
1540             resolveChannelFuture.channel().close();
1541         }
1542     }
1543 
1544     private static final class DnsResolveChannelPerResolutionProvider implements DnsResolveChannelProvider {
1545 
1546         private final Bootstrap bootstrap;
1547         private final SocketAddress localAddress;
1548 
1549         DnsResolveChannelPerResolutionProvider(Bootstrap bootstrap, SocketAddress localAddress) {
1550             this.bootstrap = bootstrap;
1551             this.localAddress = localAddress;
1552         }
1553 
1554         @Override
1555         public <T> ChannelFuture nextResolveChannel(Future<T> resolutionFuture) {
1556             final ChannelFuture f = registerOrBind(bootstrap, localAddress);
1557             resolutionFuture.addListener(new FutureListener<T>() {
1558                 @Override
1559                 public void operationComplete(Future<T> future) {
1560                     // Always just close the Channel once the resolution is considered complete.
1561                     f.channel().close();
1562                 }
1563             });
1564             return f;
1565         }
1566 
1567         @Override
1568         public void close() {
1569             // NOOP
1570         }
1571     }
1572 }