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