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