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