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