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