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