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