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