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