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