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