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.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.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.Collection;
79 import java.util.Collections;
80 import java.util.Comparator;
81 import java.util.Enumeration;
82 import java.util.HashMap;
83 import java.util.Iterator;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.concurrent.TimeUnit;
87
88 import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT;
89 import static io.netty.util.internal.ObjectUtil.checkNotNull;
90 import static io.netty.util.internal.ObjectUtil.checkPositive;
91
92
93
94
95 public class DnsNameResolver extends InetNameResolver {
96
97
98
99 public static final AttributeKey<Boolean> DNS_PIPELINE_ATTRIBUTE =
100 AttributeKey.newInstance("io.netty.resolver.dns.pipeline");
101
102 private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class);
103 private static final String LOCALHOST = "localhost";
104 private static final String WINDOWS_HOST_NAME;
105 private static final InetAddress LOCALHOST_ADDRESS;
106 private static final DnsRecord[] EMPTY_ADDITIONALS = new DnsRecord[0];
107 private static final DnsRecordType[] IPV4_ONLY_RESOLVED_RECORD_TYPES =
108 {DnsRecordType.A};
109 private static final SocketProtocolFamily[] IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES =
110 {SocketProtocolFamily.INET};
111 private static final DnsRecordType[] IPV4_PREFERRED_RESOLVED_RECORD_TYPES =
112 {DnsRecordType.A, DnsRecordType.AAAA};
113 private static final SocketProtocolFamily[] IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
114 {SocketProtocolFamily.INET, SocketProtocolFamily.INET6};
115 private static final DnsRecordType[] IPV6_ONLY_RESOLVED_RECORD_TYPES =
116 {DnsRecordType.AAAA};
117 private static final SocketProtocolFamily[] IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES =
118 {SocketProtocolFamily.INET6};
119 private static final DnsRecordType[] IPV6_PREFERRED_RESOLVED_RECORD_TYPES =
120 {DnsRecordType.AAAA, DnsRecordType.A};
121 private static final SocketProtocolFamily[] IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
122 {SocketProtocolFamily.INET6, SocketProtocolFamily.INET};
123
124 private static final ChannelHandler NOOP_HANDLER = new ChannelHandlerAdapter() {
125 @Override
126 public boolean isSharable() {
127 return true;
128 }
129 };
130
131 static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES;
132 static final String[] DEFAULT_SEARCH_DOMAINS;
133 private static final UnixResolverOptions DEFAULT_OPTIONS;
134
135 static {
136 if (NetUtil.isIpV4StackPreferred() || !anyInterfaceSupportsIpV6()) {
137 DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_ONLY;
138 LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
139 } else {
140 if (NetUtil.isIpV6AddressesPreferred()) {
141 DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV6_PREFERRED;
142 LOCALHOST_ADDRESS = NetUtil.LOCALHOST6;
143 } else {
144 DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_PREFERRED;
145 LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
146 }
147 }
148 logger.debug("Default ResolvedAddressTypes: {}", DEFAULT_RESOLVE_ADDRESS_TYPES);
149 logger.debug("Localhost address: {}", LOCALHOST_ADDRESS);
150
151 String hostName;
152 try {
153 hostName = PlatformDependent.isWindows() ? InetAddress.getLocalHost().getHostName() : null;
154 } catch (Exception ignore) {
155 hostName = null;
156 }
157 WINDOWS_HOST_NAME = hostName;
158 logger.debug("Windows hostname: {}", WINDOWS_HOST_NAME);
159
160 String[] searchDomains;
161 try {
162 List<String> list = PlatformDependent.isWindows()
163 ? getSearchDomainsHack()
164 : UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains();
165 searchDomains = list.toArray(EmptyArrays.EMPTY_STRINGS);
166 } catch (Exception ignore) {
167
168 searchDomains = EmptyArrays.EMPTY_STRINGS;
169 }
170 DEFAULT_SEARCH_DOMAINS = searchDomains;
171 logger.debug("Default search domains: {}", Arrays.toString(DEFAULT_SEARCH_DOMAINS));
172
173 UnixResolverOptions options;
174 try {
175 options = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions();
176 } catch (Exception ignore) {
177 options = UnixResolverOptions.newBuilder().build();
178 }
179 DEFAULT_OPTIONS = options;
180 logger.debug("Default {}", DEFAULT_OPTIONS);
181 }
182
183
184
185
186 private static boolean anyInterfaceSupportsIpV6() {
187 for (NetworkInterface iface : NetUtil.NETWORK_INTERFACES) {
188 Enumeration<InetAddress> addresses = iface.getInetAddresses();
189 while (addresses.hasMoreElements()) {
190 InetAddress inetAddress = addresses.nextElement();
191 if (inetAddress instanceof Inet6Address && !inetAddress.isAnyLocalAddress() &&
192 !inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
193 return true;
194 }
195 }
196 }
197 return false;
198 }
199
200 @SuppressWarnings("unchecked")
201 private static List<String> getSearchDomainsHack() throws Exception {
202
203
204 if (PlatformDependent.javaVersion() < 9) {
205
206
207 Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
208 Method open = configClass.getMethod("open");
209 Method nameservers = configClass.getMethod("searchlist");
210 Object instance = open.invoke(null);
211
212 return (List<String>) nameservers.invoke(instance);
213 }
214 return Collections.emptyList();
215 }
216
217 private static final DatagramDnsResponseDecoder DATAGRAM_DECODER = new DatagramDnsResponseDecoder() {
218 @Override
219 protected DnsResponse decodeResponse(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
220 DnsResponse response = super.decodeResponse(ctx, packet);
221 if (packet.content().isReadable()) {
222
223
224
225 response.setTruncated(true);
226
227 if (logger.isDebugEnabled()) {
228 logger.debug("{} RECEIVED: UDP [{}: {}] truncated packet received, consider adjusting "
229 + "maxPayloadSize for the {}.", ctx.channel(), response.id(), packet.sender(),
230 StringUtil.simpleClassName(DnsNameResolver.class));
231 }
232 }
233 return response;
234 }
235 };
236 private static final DatagramDnsQueryEncoder DATAGRAM_ENCODER = new DatagramDnsQueryEncoder();
237
238 private final Promise<Channel> channelReadyPromise;
239 private final Channel ch;
240
241
242 private final Comparator<InetSocketAddress> nameServerComparator;
243
244
245
246 private final DnsQueryContextManager queryContextManager = new DnsQueryContextManager();
247
248
249
250
251 private final DnsCache resolveCache;
252 private final AuthoritativeDnsServerCache authoritativeDnsServerCache;
253 private final DnsCnameCache cnameCache;
254 private final DnsServerAddressStream queryDnsServerAddressStream;
255
256 private final long queryTimeoutMillis;
257 private final int maxQueriesPerResolve;
258 private final ResolvedAddressTypes resolvedAddressTypes;
259 private final SocketProtocolFamily[] resolvedInternetProtocolFamilies;
260 private final boolean recursionDesired;
261 private final int maxPayloadSize;
262 private final boolean optResourceEnabled;
263 private final HostsFileEntriesResolver hostsFileEntriesResolver;
264 private final DnsServerAddressStreamProvider dnsServerAddressStreamProvider;
265 private final String[] searchDomains;
266 private final int ndots;
267 private final boolean supportsAAAARecords;
268 private final boolean supportsARecords;
269 private final SocketProtocolFamily preferredAddressType;
270 private final DnsRecordType[] resolveRecordTypes;
271 private final boolean decodeIdn;
272 private final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory;
273 private final boolean completeOncePreferredResolved;
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);
385 }
386
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 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) {
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(addressType(preferredAddressType));
484 this.maxNumConsolidation = maxNumConsolidation;
485 if (maxNumConsolidation > 0) {
486 inflightLookups = new HashMap<String, Future<List<InetAddress>>>();
487 } else {
488 inflightLookups = null;
489 }
490
491 Bootstrap b = new Bootstrap()
492 .group(executor())
493 .channelFactory(channelFactory)
494 .attr(DNS_PIPELINE_ATTRIBUTE, Boolean.TRUE);
495 this.channelReadyPromise = executor().newPromise();
496 final DnsResponseHandler responseHandler =
497 new DnsResponseHandler(channelReadyPromise);
498 b.handler(new ChannelInitializer<DatagramChannel>() {
499 @Override
500 protected void initChannel(DatagramChannel ch) {
501 ch.pipeline().addLast(DATAGRAM_ENCODER, DATAGRAM_DECODER, responseHandler);
502 }
503 });
504
505 if (localAddress == null) {
506 localAddress = new InetSocketAddress(0);
507 }
508
509 ChannelFuture future = b.bind(localAddress);
510 if (future.isDone()) {
511 Throwable cause = future.cause();
512 if (cause != null) {
513 if (cause instanceof RuntimeException) {
514 throw (RuntimeException) cause;
515 }
516 if (cause instanceof Error) {
517 throw (Error) cause;
518 }
519 throw new IllegalStateException("Unable to create / register Channel", cause);
520 }
521 } else {
522 future.addListener(new ChannelFutureListener() {
523 @Override
524 public void operationComplete(ChannelFuture future) {
525 Throwable cause = future.cause();
526 if (cause != null) {
527 channelReadyPromise.tryFailure(cause);
528 }
529 }
530 });
531 }
532 ch = future.channel();
533 ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
534
535 ch.closeFuture().addListener(new ChannelFutureListener() {
536 @Override
537 public void operationComplete(ChannelFuture future) {
538 resolveCache.clear();
539 cnameCache.clear();
540 authoritativeDnsServerCache.clear();
541 }
542 });
543 }
544
545 static SocketProtocolFamily preferredAddressType(ResolvedAddressTypes resolvedAddressTypes) {
546 switch (resolvedAddressTypes) {
547 case IPV4_ONLY:
548 case IPV4_PREFERRED:
549 return SocketProtocolFamily.INET;
550 case IPV6_ONLY:
551 case IPV6_PREFERRED:
552 return SocketProtocolFamily.INET6;
553 default:
554 throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
555 }
556 }
557
558
559 InetSocketAddress newRedirectServerAddress(InetAddress server) {
560 return new InetSocketAddress(server, DNS_PORT);
561 }
562
563 final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory() {
564 return dnsQueryLifecycleObserverFactory;
565 }
566
567
568
569
570
571
572
573
574
575
576
577
578 protected DnsServerAddressStream newRedirectDnsServerStream(
579 @SuppressWarnings("unused") String hostname, List<InetSocketAddress> nameservers) {
580 DnsServerAddressStream cached = authoritativeDnsServerCache().get(hostname);
581 if (cached == null || cached.size() == 0) {
582
583
584 Collections.sort(nameservers, nameServerComparator);
585 return new SequentialDnsServerAddressStream(nameservers, 0);
586 }
587 return cached;
588 }
589
590
591
592
593 public DnsCache resolveCache() {
594 return resolveCache;
595 }
596
597
598
599
600 public DnsCnameCache cnameCache() {
601 return cnameCache;
602 }
603
604
605
606
607 public AuthoritativeDnsServerCache authoritativeDnsServerCache() {
608 return authoritativeDnsServerCache;
609 }
610
611
612
613
614
615 public long queryTimeoutMillis() {
616 return queryTimeoutMillis;
617 }
618
619
620
621
622 public DnsServerAddressStream queryDnsServerAddressStream() {
623 return queryDnsServerAddressStream;
624 }
625
626
627
628
629
630 public ResolvedAddressTypes resolvedAddressTypes() {
631 return resolvedAddressTypes;
632 }
633
634 SocketProtocolFamily[] resolvedInternetProtocolFamiliesUnsafe() {
635 return resolvedInternetProtocolFamilies;
636 }
637
638 final String[] searchDomains() {
639 return searchDomains;
640 }
641
642 final int ndots() {
643 return ndots;
644 }
645
646 final boolean supportsAAAARecords() {
647 return supportsAAAARecords;
648 }
649
650 final boolean supportsARecords() {
651 return supportsARecords;
652 }
653
654 final SocketProtocolFamily preferredAddressType() {
655 return preferredAddressType;
656 }
657
658 final DnsRecordType[] resolveRecordTypes() {
659 return resolveRecordTypes;
660 }
661
662 final boolean isDecodeIdn() {
663 return decodeIdn;
664 }
665
666
667
668
669
670 public boolean isRecursionDesired() {
671 return recursionDesired;
672 }
673
674
675
676
677
678 public int maxQueriesPerResolve() {
679 return maxQueriesPerResolve;
680 }
681
682
683
684
685 public int maxPayloadSize() {
686 return maxPayloadSize;
687 }
688
689
690
691
692
693 public boolean isOptResourceEnabled() {
694 return optResourceEnabled;
695 }
696
697
698
699
700
701 public HostsFileEntriesResolver hostsFileEntriesResolver() {
702 return hostsFileEntriesResolver;
703 }
704
705
706
707
708
709
710 @Override
711 public void close() {
712 if (ch.isOpen()) {
713 ch.close();
714 }
715 }
716
717 @Override
718 protected EventLoop executor() {
719 return (EventLoop) super.executor();
720 }
721
722 private InetAddress resolveHostsFileEntry(String hostname) {
723 if (hostsFileEntriesResolver == null) {
724 return null;
725 }
726 InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes);
727 return address == null && isLocalWindowsHost(hostname) ? LOCALHOST_ADDRESS : address;
728 }
729
730 private List<InetAddress> resolveHostsFileEntries(String hostname) {
731 if (hostsFileEntriesResolver == null) {
732 return null;
733 }
734 List<InetAddress> addresses;
735 if (hostsFileEntriesResolver instanceof DefaultHostsFileEntriesResolver) {
736 addresses = ((DefaultHostsFileEntriesResolver) hostsFileEntriesResolver)
737 .addresses(hostname, resolvedAddressTypes);
738 } else {
739 InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes);
740 addresses = address != null ? Collections.singletonList(address) : null;
741 }
742 return addresses == null && isLocalWindowsHost(hostname) ?
743 Collections.singletonList(LOCALHOST_ADDRESS) : addresses;
744 }
745
746
747
748
749
750
751
752
753 private static boolean isLocalWindowsHost(String hostname) {
754 return PlatformDependent.isWindows() &&
755 (LOCALHOST.equalsIgnoreCase(hostname) ||
756 (WINDOWS_HOST_NAME != null && WINDOWS_HOST_NAME.equalsIgnoreCase(hostname)));
757 }
758
759
760
761
762
763
764
765
766
767 public final Future<InetAddress> resolve(String inetHost, Iterable<DnsRecord> additionals) {
768 return resolve(inetHost, additionals, executor().<InetAddress>newPromise());
769 }
770
771
772
773
774
775
776
777
778
779
780 public final Future<InetAddress> resolve(String inetHost, Iterable<DnsRecord> additionals,
781 Promise<InetAddress> promise) {
782 checkNotNull(promise, "promise");
783 DnsRecord[] additionalsArray = toArray(additionals, true);
784 try {
785 doResolve(inetHost, additionalsArray, promise, resolveCache);
786 return promise;
787 } catch (Exception e) {
788 return promise.setFailure(e);
789 }
790 }
791
792
793
794
795
796
797
798
799
800 public final Future<List<InetAddress>> resolveAll(String inetHost, Iterable<DnsRecord> additionals) {
801 return resolveAll(inetHost, additionals, executor().<List<InetAddress>>newPromise());
802 }
803
804
805
806
807
808
809
810
811
812
813 public final Future<List<InetAddress>> resolveAll(String inetHost, Iterable<DnsRecord> additionals,
814 Promise<List<InetAddress>> promise) {
815 checkNotNull(promise, "promise");
816 DnsRecord[] additionalsArray = toArray(additionals, true);
817 try {
818 doResolveAll(inetHost, additionalsArray, promise, resolveCache);
819 return promise;
820 } catch (Exception e) {
821 return promise.setFailure(e);
822 }
823 }
824
825 @Override
826 protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
827 doResolve(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841 public final Future<List<DnsRecord>> resolveAll(DnsQuestion question) {
842 return resolveAll(question, EMPTY_ADDITIONALS, executor().<List<DnsRecord>>newPromise());
843 }
844
845
846
847
848
849
850
851
852
853
854
855
856
857 public final Future<List<DnsRecord>> resolveAll(DnsQuestion question, Iterable<DnsRecord> additionals) {
858 return resolveAll(question, additionals, executor().<List<DnsRecord>>newPromise());
859 }
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874 public final Future<List<DnsRecord>> resolveAll(DnsQuestion question, Iterable<DnsRecord> additionals,
875 Promise<List<DnsRecord>> promise) {
876 final DnsRecord[] additionalsArray = toArray(additionals, true);
877 return resolveAll(question, additionalsArray, promise);
878 }
879
880 private Future<List<DnsRecord>> resolveAll(DnsQuestion question, DnsRecord[] additionals,
881 Promise<List<DnsRecord>> promise) {
882 checkNotNull(question, "question");
883 checkNotNull(promise, "promise");
884
885
886 final DnsRecordType type = question.type();
887 final String hostname = question.name();
888
889 if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
890 final List<InetAddress> hostsFileEntries = resolveHostsFileEntries(hostname);
891 if (hostsFileEntries != null) {
892 List<DnsRecord> result = new ArrayList<DnsRecord>();
893 for (InetAddress hostsFileEntry : hostsFileEntries) {
894 ByteBuf content = null;
895 if (hostsFileEntry instanceof Inet4Address) {
896 if (type == DnsRecordType.A) {
897 content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
898 }
899 } else if (hostsFileEntry instanceof Inet6Address) {
900 if (type == DnsRecordType.AAAA) {
901 content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
902 }
903 }
904 if (content != null) {
905
906
907 result.add(new DefaultDnsRawRecord(hostname, type, 86400, content));
908 }
909 }
910
911 if (!result.isEmpty()) {
912 if (!trySuccess(promise, result)) {
913
914 for (DnsRecord r: result) {
915 ReferenceCountUtil.safeRelease(r);
916 }
917 }
918 return promise;
919 }
920 }
921 }
922
923
924 final DnsServerAddressStream nameServerAddrs =
925 dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
926 new DnsRecordResolveContext(this, ch, channelReadyPromise, promise, question, additionals,
927 nameServerAddrs, maxQueriesPerResolve).resolve(promise);
928 return promise;
929 }
930
931 private static DnsRecord[] toArray(Iterable<DnsRecord> additionals, boolean validateType) {
932 checkNotNull(additionals, "additionals");
933 if (additionals instanceof Collection) {
934 Collection<DnsRecord> records = (Collection<DnsRecord>) additionals;
935 for (DnsRecord r: additionals) {
936 validateAdditional(r, validateType);
937 }
938 return records.toArray(new DnsRecord[records.size()]);
939 }
940
941 Iterator<DnsRecord> additionalsIt = additionals.iterator();
942 if (!additionalsIt.hasNext()) {
943 return EMPTY_ADDITIONALS;
944 }
945 List<DnsRecord> records = new ArrayList<DnsRecord>();
946 do {
947 DnsRecord r = additionalsIt.next();
948 validateAdditional(r, validateType);
949 records.add(r);
950 } while (additionalsIt.hasNext());
951
952 return records.toArray(new DnsRecord[records.size()]);
953 }
954
955 private static void validateAdditional(DnsRecord record, boolean validateType) {
956 checkNotNull(record, "record");
957 if (validateType && record instanceof DnsRawRecord) {
958 throw new IllegalArgumentException("DnsRawRecord implementations not allowed: " + record);
959 }
960 }
961
962 private InetAddress loopbackAddress() {
963 switch (preferredAddressType()) {
964 case INET:
965 return NetUtil.LOCALHOST4;
966 case INET6:
967 return NetUtil.LOCALHOST6;
968 default:
969 throw new UnsupportedOperationException("Only INET and INET6 are supported");
970 }
971 }
972
973
974
975
976
977 protected void doResolve(String inetHost,
978 DnsRecord[] additionals,
979 Promise<InetAddress> promise,
980 DnsCache resolveCache) throws Exception {
981 if (inetHost == null || inetHost.isEmpty()) {
982
983 promise.setSuccess(loopbackAddress());
984 return;
985 }
986 final InetAddress address = NetUtil.createInetAddressFromIpAddressString(inetHost);
987 if (address != null) {
988
989 promise.setSuccess(address);
990 return;
991 }
992
993 final String hostname = hostname(inetHost);
994
995 InetAddress hostsFileEntry = resolveHostsFileEntry(hostname);
996 if (hostsFileEntry != null) {
997 promise.setSuccess(hostsFileEntry);
998 return;
999 }
1000
1001 if (!doResolveCached(hostname, additionals, promise, resolveCache)) {
1002 doResolveUncached(hostname, additionals, promise, resolveCache, completeOncePreferredResolved);
1003 }
1004 }
1005
1006 private boolean doResolveCached(String hostname,
1007 DnsRecord[] additionals,
1008 Promise<InetAddress> promise,
1009 DnsCache resolveCache) {
1010 final List<? extends DnsCacheEntry> cachedEntries = resolveCache.get(hostname, additionals);
1011 if (cachedEntries == null || cachedEntries.isEmpty()) {
1012 return false;
1013 }
1014
1015 Throwable cause = cachedEntries.get(0).cause();
1016 if (cause == null) {
1017 final int numEntries = cachedEntries.size();
1018
1019 for (SocketProtocolFamily f : resolvedInternetProtocolFamilies) {
1020 for (int i = 0; i < numEntries; i++) {
1021 final DnsCacheEntry e = cachedEntries.get(i);
1022 final Class<? extends InetAddress> addressType = addressType(f);
1023 if (addressType != null && addressType.isInstance(e.address())) {
1024 trySuccess(promise, e.address());
1025 return true;
1026 }
1027 }
1028 }
1029 return false;
1030 } else {
1031 tryFailure(promise, cause);
1032 return true;
1033 }
1034 }
1035
1036 static Class<? extends InetAddress> addressType(SocketProtocolFamily f) {
1037 switch (f) {
1038 case INET:
1039 return Inet4Address.class;
1040 case INET6:
1041 return Inet6Address.class;
1042 default:
1043 return null;
1044 }
1045 }
1046
1047 static <T> boolean trySuccess(Promise<T> promise, T result) {
1048 final boolean notifiedRecords = promise.trySuccess(result);
1049 if (!notifiedRecords) {
1050
1051
1052
1053 logger.trace("Failed to notify success ({}) to a promise: {}", result, promise);
1054 }
1055 return notifiedRecords;
1056 }
1057
1058 private static void tryFailure(Promise<?> promise, Throwable cause) {
1059 if (!promise.tryFailure(cause)) {
1060
1061
1062
1063 logger.trace("Failed to notify failure to a promise: {}", promise, cause);
1064 }
1065 }
1066
1067 private void doResolveUncached(String hostname,
1068 DnsRecord[] additionals,
1069 final Promise<InetAddress> promise,
1070 DnsCache resolveCache, boolean completeEarlyIfPossible) {
1071 final Promise<List<InetAddress>> allPromise = executor().newPromise();
1072 doResolveAllUncached(hostname, additionals, promise, allPromise, resolveCache, completeEarlyIfPossible);
1073 allPromise.addListener(new FutureListener<List<InetAddress>>() {
1074 @Override
1075 public void operationComplete(Future<List<InetAddress>> future) {
1076 if (future.isSuccess()) {
1077 trySuccess(promise, future.getNow().get(0));
1078 } else {
1079 tryFailure(promise, future.cause());
1080 }
1081 }
1082 });
1083 }
1084
1085 @Override
1086 protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise) throws Exception {
1087 doResolveAll(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
1088 }
1089
1090
1091
1092
1093
1094 protected void doResolveAll(String inetHost,
1095 DnsRecord[] additionals,
1096 Promise<List<InetAddress>> promise,
1097 DnsCache resolveCache) throws Exception {
1098 if (inetHost == null || inetHost.isEmpty()) {
1099
1100 promise.setSuccess(Collections.singletonList(loopbackAddress()));
1101 return;
1102 }
1103 final InetAddress address = NetUtil.createInetAddressFromIpAddressString(inetHost);
1104 if (address != null) {
1105
1106 promise.setSuccess(Collections.singletonList(address));
1107 return;
1108 }
1109
1110 final String hostname = hostname(inetHost);
1111
1112 List<InetAddress> hostsFileEntries = resolveHostsFileEntries(hostname);
1113 if (hostsFileEntries != null) {
1114 promise.setSuccess(hostsFileEntries);
1115 return;
1116 }
1117
1118 if (!doResolveAllCached(hostname, additionals, promise, resolveCache, resolvedInternetProtocolFamilies)) {
1119 doResolveAllUncached(hostname, additionals, promise, promise,
1120 resolveCache, completeOncePreferredResolved);
1121 }
1122 }
1123
1124 static boolean doResolveAllCached(String hostname,
1125 DnsRecord[] additionals,
1126 Promise<List<InetAddress>> promise,
1127 DnsCache resolveCache,
1128 SocketProtocolFamily[] resolvedInternetProtocolFamilies) {
1129 final List<? extends DnsCacheEntry> cachedEntries = resolveCache.get(hostname, additionals);
1130 if (cachedEntries == null || cachedEntries.isEmpty()) {
1131 return false;
1132 }
1133
1134 Throwable cause = cachedEntries.get(0).cause();
1135 if (cause == null) {
1136 List<InetAddress> result = null;
1137 final int numEntries = cachedEntries.size();
1138 for (SocketProtocolFamily f : resolvedInternetProtocolFamilies) {
1139 for (int i = 0; i < numEntries; i++) {
1140 final DnsCacheEntry e = cachedEntries.get(i);
1141 Class<? extends InetAddress> addressType = addressType(f);
1142 if (addressType != null && addressType.isInstance(e.address())) {
1143 if (result == null) {
1144 result = new ArrayList<InetAddress>(numEntries);
1145 }
1146 result.add(e.address());
1147 }
1148 }
1149 }
1150 if (result != null) {
1151 trySuccess(promise, result);
1152 return true;
1153 }
1154 return false;
1155 } else {
1156 tryFailure(promise, cause);
1157 return true;
1158 }
1159 }
1160
1161 private void doResolveAllUncached(final String hostname,
1162 final DnsRecord[] additionals,
1163 final Promise<?> originalPromise,
1164 final Promise<List<InetAddress>> promise,
1165 final DnsCache resolveCache,
1166 final boolean completeEarlyIfPossible) {
1167
1168
1169 EventExecutor executor = executor();
1170 if (executor.inEventLoop()) {
1171 doResolveAllUncached0(hostname, additionals, originalPromise,
1172 promise, resolveCache, completeEarlyIfPossible);
1173 } else {
1174 executor.execute(new Runnable() {
1175 @Override
1176 public void run() {
1177 doResolveAllUncached0(hostname, additionals, originalPromise,
1178 promise, resolveCache, completeEarlyIfPossible);
1179 }
1180 });
1181 }
1182 }
1183
1184 private void doResolveAllUncached0(final String hostname,
1185 final DnsRecord[] additionals,
1186 final Promise<?> originalPromise,
1187 final Promise<List<InetAddress>> promise,
1188 final DnsCache resolveCache,
1189 final boolean completeEarlyIfPossible) {
1190
1191 assert executor().inEventLoop();
1192
1193 if (inflightLookups != null && (additionals == null || additionals.length == 0)) {
1194 Future<List<InetAddress>> inflightFuture = inflightLookups.get(hostname);
1195 if (inflightFuture != null) {
1196 inflightFuture.addListener(new GenericFutureListener<Future<? super List<InetAddress>>>() {
1197 @SuppressWarnings("unchecked")
1198 @Override
1199 public void operationComplete(Future<? super List<InetAddress>> future) {
1200 if (future.isSuccess()) {
1201 promise.setSuccess((List<InetAddress>) future.getNow());
1202 } else {
1203 Throwable cause = future.cause();
1204 if (isTimeoutError(cause)) {
1205
1206
1207
1208
1209 resolveNow(hostname, additionals, originalPromise, promise,
1210 resolveCache, completeEarlyIfPossible);
1211 } else {
1212 promise.setFailure(cause);
1213 }
1214 }
1215 }
1216 });
1217 return;
1218
1219 } else if (inflightLookups.size() < maxNumConsolidation) {
1220 inflightLookups.put(hostname, promise);
1221 promise.addListener(new GenericFutureListener<Future<? super List<InetAddress>>>() {
1222 @Override
1223 public void operationComplete(Future<? super List<InetAddress>> future) {
1224 inflightLookups.remove(hostname);
1225 }
1226 });
1227 }
1228 }
1229 resolveNow(hostname, additionals, originalPromise, promise, resolveCache, completeEarlyIfPossible);
1230 }
1231
1232 private void resolveNow(final String hostname,
1233 final DnsRecord[] additionals,
1234 final Promise<?> originalPromise,
1235 final Promise<List<InetAddress>> promise,
1236 final DnsCache resolveCache,
1237 final boolean completeEarlyIfPossible) {
1238 final DnsServerAddressStream nameServerAddrs =
1239 dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
1240 DnsAddressResolveContext ctx = new DnsAddressResolveContext(this, ch, channelReadyPromise,
1241 originalPromise, hostname, additionals, nameServerAddrs, maxQueriesPerResolve, resolveCache,
1242 authoritativeDnsServerCache, completeEarlyIfPossible);
1243 ctx.resolve(promise);
1244 }
1245
1246 private static String hostname(String inetHost) {
1247 String hostname = IDN.toASCII(inetHost);
1248
1249 if (StringUtil.endsWith(inetHost, '.') && !StringUtil.endsWith(hostname, '.')) {
1250 hostname += ".";
1251 }
1252 return hostname;
1253 }
1254
1255
1256
1257
1258 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(DnsQuestion question) {
1259 return query(nextNameServerAddress(), question);
1260 }
1261
1262
1263
1264
1265 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1266 DnsQuestion question, Iterable<DnsRecord> additionals) {
1267 return query(nextNameServerAddress(), question, additionals);
1268 }
1269
1270
1271
1272
1273 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1274 DnsQuestion question, Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1275 return query(nextNameServerAddress(), question, Collections.<DnsRecord>emptyList(), promise);
1276 }
1277
1278 private InetSocketAddress nextNameServerAddress() {
1279 return queryDnsServerAddressStream.next();
1280 }
1281
1282
1283
1284
1285 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1286 InetSocketAddress nameServerAddr, DnsQuestion question) {
1287
1288 return doQuery(ch, channelReadyPromise, nameServerAddr, question, NoopDnsQueryLifecycleObserver.INSTANCE,
1289 EMPTY_ADDITIONALS, true,
1290 ch.eventLoop().<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>>newPromise());
1291 }
1292
1293
1294
1295
1296 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1297 InetSocketAddress nameServerAddr, DnsQuestion question, Iterable<DnsRecord> additionals) {
1298
1299 return doQuery(ch, channelReadyPromise, nameServerAddr, question, NoopDnsQueryLifecycleObserver.INSTANCE,
1300 toArray(additionals, false), true,
1301 ch.eventLoop().<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>>newPromise());
1302 }
1303
1304
1305
1306
1307 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1308 InetSocketAddress nameServerAddr, DnsQuestion question,
1309 Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1310
1311 return doQuery(ch, channelReadyPromise, nameServerAddr, question, NoopDnsQueryLifecycleObserver.INSTANCE,
1312 EMPTY_ADDITIONALS, true, promise);
1313 }
1314
1315
1316
1317
1318 public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
1319 InetSocketAddress nameServerAddr, DnsQuestion question,
1320 Iterable<DnsRecord> additionals,
1321 Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1322
1323 return doQuery(ch, channelReadyPromise, nameServerAddr, question, NoopDnsQueryLifecycleObserver.INSTANCE,
1324 toArray(additionals, false), true, promise);
1325 }
1326
1327
1328
1329
1330
1331
1332 public static boolean isTransportOrTimeoutError(Throwable cause) {
1333 return cause != null && cause.getCause() instanceof DnsNameResolverException;
1334 }
1335
1336
1337
1338
1339
1340
1341 public static boolean isTimeoutError(Throwable cause) {
1342 return cause != null && cause.getCause() instanceof DnsNameResolverTimeoutException;
1343 }
1344
1345 final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> doQuery(
1346 Channel channel, Future<? extends Channel> channelReadyFuture,
1347 InetSocketAddress nameServerAddr, DnsQuestion question,
1348 final DnsQueryLifecycleObserver queryLifecycleObserver,
1349 DnsRecord[] additionals, boolean flush,
1350 Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
1351
1352 final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> castPromise = cast(
1353 checkNotNull(promise, "promise"));
1354 final int payloadSize = isOptResourceEnabled() ? maxPayloadSize() : 0;
1355 try {
1356 DnsQueryContext queryContext = new DatagramDnsQueryContext(channel, channelReadyFuture, nameServerAddr,
1357 queryContextManager, payloadSize, isRecursionDesired(), queryTimeoutMillis(), question, additionals,
1358 castPromise, socketBootstrap, retryWithTcpOnTimeout);
1359 ChannelFuture future = queryContext.writeQuery(flush);
1360 queryLifecycleObserver.queryWritten(nameServerAddr, future);
1361 return castPromise;
1362 } catch (Exception e) {
1363 return castPromise.setFailure(e);
1364 }
1365 }
1366
1367 @SuppressWarnings("unchecked")
1368 private static Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> cast(Promise<?> promise) {
1369 return (Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>>) promise;
1370 }
1371
1372 final DnsServerAddressStream newNameServerAddressStream(String hostname) {
1373 return dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
1374 }
1375
1376 private final class DnsResponseHandler extends ChannelInboundHandlerAdapter {
1377
1378 private final Promise<Channel> channelActivePromise;
1379
1380 DnsResponseHandler(Promise<Channel> channelActivePromise) {
1381 this.channelActivePromise = channelActivePromise;
1382 }
1383
1384 @Override
1385 public void channelRead(ChannelHandlerContext ctx, Object msg) {
1386 final Channel qCh = ctx.channel();
1387 final DatagramDnsResponse res = (DatagramDnsResponse) msg;
1388 final int queryId = res.id();
1389 logger.debug("{} RECEIVED: UDP [{}: {}], {}", qCh, queryId, res.sender(), res);
1390
1391 final DnsQueryContext qCtx = queryContextManager.get(res.sender(), queryId);
1392 if (qCtx == null) {
1393 logger.debug("{} Received a DNS response with an unknown ID: UDP [{}: {}]",
1394 qCh, queryId, res.sender());
1395 res.release();
1396 return;
1397 } else if (qCtx.isDone()) {
1398 logger.debug("{} Received a DNS response for a query that was timed out or cancelled: UDP [{}: {}]",
1399 qCh, queryId, res.sender());
1400 res.release();
1401 return;
1402 }
1403
1404
1405 qCtx.finishSuccess(res, res.isTruncated());
1406 }
1407
1408 @Override
1409 public void channelActive(ChannelHandlerContext ctx) throws Exception {
1410 super.channelActive(ctx);
1411 channelActivePromise.trySuccess(ctx.channel());
1412 }
1413
1414 @Override
1415 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
1416 if (cause instanceof CorruptedFrameException) {
1417 logger.debug("{} Unable to decode DNS response: UDP", ctx.channel(), cause);
1418 } else {
1419 logger.warn("{} Unexpected exception: UDP", ctx.channel(), cause);
1420 }
1421 }
1422 }
1423 }