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