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