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