Simplified resolver via DnsResponse->DnsEnvelope for all promise types

This commit is contained in:
nathan 2018-01-29 00:49:17 +01:00
parent dc54c9b226
commit 12c7c9f0a5
6 changed files with 231 additions and 262 deletions

View File

@ -0,0 +1,109 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns;
import java.io.IOException;
import java.net.InetSocketAddress;
import dorkbox.network.dns.records.DnsMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AddressedEnvelope;
/**
*
*/
public
class DnsEnvelope extends DnsMessage implements AddressedEnvelope<DnsEnvelope, InetSocketAddress> {
private InetSocketAddress localAddress;
private InetSocketAddress remoteAddress;
public
DnsEnvelope() {
super();
}
public
DnsEnvelope(final ByteBuf buffer, final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IOException {
super(buffer);
this.localAddress = localAddress;
this.remoteAddress = remoteAddress;
}
public
DnsEnvelope(final DnsInput input, final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IOException {
super(input);
this.localAddress = localAddress;
this.remoteAddress = remoteAddress;
}
public
void setLocalAddress(final InetSocketAddress localAddress) {
this.localAddress = localAddress;
}
public
void setRemoteAddress(final InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
@Override
public
DnsEnvelope content() {
return this;
}
@Override
public final
InetSocketAddress sender() {
return localAddress;
}
@Override
public final
InetSocketAddress recipient() {
return remoteAddress;
}
@Override
public
DnsEnvelope touch() {
return (DnsEnvelope) super.touch();
}
@Override
public
DnsEnvelope touch(Object hint) {
return (DnsEnvelope) super.touch(hint);
}
@Override
public
DnsEnvelope retain() {
return (DnsEnvelope) super.retain();
}
@Override
public
DnsEnvelope retain(int increment) {
return (DnsEnvelope) super.retain(increment);
}
}

View File

@ -6,12 +6,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Locale; import java.util.Locale;
import dorkbox.network.dns.constants.DnsClass; import dorkbox.network.dns.constants.*;
import dorkbox.network.dns.constants.DnsOpCode;
import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.constants.Flags;
import dorkbox.network.dns.records.DnsMessage;
import dorkbox.network.dns.records.DnsRecord; import dorkbox.network.dns.records.DnsRecord;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.AddressedEnvelope;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -20,29 +15,7 @@ import io.netty.util.internal.StringUtil;
* *
*/ */
public public
class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, InetSocketAddress> { class DnsQuestion extends DnsEnvelope {
private InetSocketAddress recipient;
private final boolean isResolveQuestion;
/**
* Creates a new instance.
*
* @param isResolveQuestion true if it's a resolve question, which means we ALSO are going to keep resolving names until we get an IP
* address.
*/
private
DnsQuestion(final boolean isResolveQuestion) {
this.isResolveQuestion = isResolveQuestion;
this.recipient = null;
}
public
boolean isResolveQuestion() {
return isResolveQuestion;
}
public static public static
DnsQuestion newResolveQuestion(final String inetHost, final int type, final boolean isRecursionDesired) { DnsQuestion newResolveQuestion(final String inetHost, final int type, final boolean isRecursionDesired) {
return newQuestion(inetHost, type, isRecursionDesired, true); return newQuestion(inetHost, type, isRecursionDesired, true);
@ -53,7 +26,6 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, I
return newQuestion(inetHost, type, isRecursionDesired, false); return newQuestion(inetHost, type, isRecursionDesired, false);
} }
private static private static
DnsQuestion newQuestion(final String inetHost, final int type, final boolean isRecursionDesired, boolean isResolveQuestion) { DnsQuestion newQuestion(final String inetHost, final int type, final boolean isRecursionDesired, boolean isResolveQuestion) {
@ -63,6 +35,11 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, I
// - https://github.com/netty/netty/issues/4935 // - https://github.com/netty/netty/issues/4935
String hostName = hostNameAsciiFix(checkNotNull(inetHost, "hostname")); String hostName = hostNameAsciiFix(checkNotNull(inetHost, "hostname"));
if (hostName == null) {
// hostNameAsciiFix can throw a TextParseException if it fails to parse
return null;
}
hostName = hostName.toLowerCase(Locale.US); hostName = hostName.toLowerCase(Locale.US);
@ -103,7 +80,7 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, I
public static public static
String hostNameAsciiFix(String inetHost) { String hostNameAsciiFix(String inetHost) {
try { try {
String hostName = java.net.IDN.toASCII(inetHost); String hostName = java.net.IDN.toASCII(inetHost); // can throw IllegalArgumentException
// Check for http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6894622 // Check for http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6894622
if (StringUtil.endsWith(inetHost, '.') && !StringUtil.endsWith(hostName, '.')) { if (StringUtil.endsWith(inetHost, '.') && !StringUtil.endsWith(hostName, '.')) {
@ -118,54 +95,46 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, I
return null; return null;
} }
private final boolean isResolveQuestion;
/**
* Creates a new instance.
*
* @param isResolveQuestion true if it's a resolve question, which means we ALSO are going to keep resolving names until we get an IP
* address.
*/
private
DnsQuestion(final boolean isResolveQuestion) {
super();
this.isResolveQuestion = isResolveQuestion;
}
public
boolean isResolveQuestion() {
return isResolveQuestion;
}
public public
void init(int id, InetSocketAddress recipient) { void init(int id, InetSocketAddress recipient) {
getHeader().setID(id); getHeader().setID(id);
this.recipient = recipient; setRemoteAddress(recipient);
} }
@Override @Override
public public
DnsQuestion content() { int hashCode() {
return this; int hashCode = super.hashCode();
} if (sender() != null) {
hashCode = hashCode * 31 + sender().hashCode();
@Override }
public if (recipient() != null) {
InetSocketAddress sender() { hashCode = hashCode * 31 + recipient().hashCode();
return null; }
} return hashCode;
@Override
public
InetSocketAddress recipient() {
return recipient;
}
@Override
public
DnsQuestion touch() {
return (DnsQuestion) super.touch();
}
@Override
public
DnsQuestion touch(Object hint) {
return (DnsQuestion) super.touch(hint);
}
@Override
public
DnsQuestion retain() {
return (DnsQuestion) super.retain();
}
@Override
public
DnsQuestion retain(int increment) {
return (DnsQuestion) super.retain(increment);
} }
@Override @Override
@ -205,18 +174,5 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope<DnsQuestion, I
return true; return true;
} }
@Override
public
int hashCode() {
int hashCode = super.hashCode();
if (sender() != null) {
hashCode = hashCode * 31 + sender().hashCode();
}
if (recipient() != null) {
hashCode = hashCode * 31 + recipient().hashCode();
}
return hashCode;
}
} }

View File

@ -19,7 +19,6 @@ import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import dorkbox.network.dns.records.DnsMessage;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.AddressedEnvelope;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
@ -27,74 +26,40 @@ import io.netty.util.internal.UnstableApi;
* A {@link DnsResponse} implementation for UDP/IP. * A {@link DnsResponse} implementation for UDP/IP.
*/ */
@UnstableApi @UnstableApi
public class DnsResponse extends DnsMessage implements AddressedEnvelope<DnsResponse, InetSocketAddress> { public
class DnsResponse extends DnsEnvelope {
private final InetSocketAddress sender;
private final InetSocketAddress recipient;
/** /**
* Creates a new instance. * Creates a new instance.
* *
* @param sender the address of the sender * @param localAddress the address of the sender
* @param recipient the address of the recipient * @param remoteAddress the address of the recipient
*/ */
public public
DnsResponse(InetSocketAddress sender, InetSocketAddress recipient, final DnsInput dnsInput) throws IOException { DnsResponse(final DnsInput dnsInput, InetSocketAddress localAddress, InetSocketAddress remoteAddress) throws IOException {
super(dnsInput); super(dnsInput, localAddress, remoteAddress);
if (recipient == null && sender == null) { if (remoteAddress == null && localAddress == null) {
throw new NullPointerException("recipient and sender"); throw new NullPointerException("localAddress and remoteAddress");
} }
this.sender = sender;
this.recipient = recipient;
} }
@Override @Override
public public
DnsResponse content() { int hashCode() {
return this; int hashCode = super.hashCode();
} if (sender() != null) {
hashCode = hashCode * 31 + sender().hashCode();
@Override }
public InetSocketAddress sender() { if (recipient() != null) {
return sender; hashCode = hashCode * 31 + recipient().hashCode();
} }
return hashCode;
@Override
public InetSocketAddress recipient() {
return recipient;
}
@Override
public
DnsResponse touch() {
return (DnsResponse) super.touch();
} }
@Override @Override
public public
DnsResponse touch(Object hint) { boolean equals(Object obj) {
return (DnsResponse) super.touch(hint);
}
@Override
public
DnsResponse retain() {
return (DnsResponse) super.retain();
}
@Override
public
DnsResponse retain(int increment) {
return (DnsResponse) super.retain(increment);
}
@Override
public boolean equals(Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;
} }
@ -113,7 +78,8 @@ public class DnsResponse extends DnsMessage implements AddressedEnvelope<DnsResp
if (that.sender() != null) { if (that.sender() != null) {
return false; return false;
} }
} else if (!sender().equals(that.sender())) { }
else if (!sender().equals(that.sender())) {
return false; return false;
} }
@ -121,22 +87,11 @@ public class DnsResponse extends DnsMessage implements AddressedEnvelope<DnsResp
if (that.recipient() != null) { if (that.recipient() != null) {
return false; return false;
} }
} else if (!recipient().equals(that.recipient())) { }
else if (!recipient().equals(that.recipient())) {
return false; return false;
} }
return true; return true;
} }
@Override
public int hashCode() {
int hashCode = super.hashCode();
if (sender() != null) {
hashCode = hashCode * 31 + sender().hashCode();
}
if (recipient() != null) {
hashCode = hashCode * 31 + recipient().hashCode();
}
return hashCode;
}
} }

View File

@ -28,29 +28,16 @@ import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import dorkbox.network.dns.DatagramDnsQueryEncoder;
import dorkbox.network.dns.DatagramDnsResponseDecoder;
import dorkbox.network.dns.DnsQuestion; import dorkbox.network.dns.DnsQuestion;
import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.DnsResponse;
import dorkbox.network.dns.clientHandlers.DatagramDnsQueryEncoder;
import dorkbox.network.dns.clientHandlers.DatagramDnsResponseDecoder;
import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.resolver.addressProvider.DefaultDnsServerAddressStreamProvider; import dorkbox.network.dns.resolver.addressProvider.*;
import dorkbox.network.dns.resolver.addressProvider.DnsServerAddressStream;
import dorkbox.network.dns.resolver.addressProvider.DnsServerAddressStreamProvider;
import dorkbox.network.dns.resolver.addressProvider.DnsServerAddresses;
import dorkbox.network.dns.resolver.addressProvider.UnixResolverDnsServerAddressStreamProvider;
import dorkbox.network.dns.resolver.cache.DnsCache; import dorkbox.network.dns.resolver.cache.DnsCache;
import dorkbox.network.dns.resolver.cache.DnsCacheEntry; import dorkbox.network.dns.resolver.cache.DnsCacheEntry;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.InternetProtocolFamily; import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.resolver.HostsFileEntriesResolver; import io.netty.resolver.HostsFileEntriesResolver;
@ -750,7 +737,7 @@ class DnsNameResolver extends InetNameResolver {
* Sends a DNS query with the specified question. * Sends a DNS query with the specified question.
*/ */
public public
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(DnsQuestion question) { Future<DnsResponse> query(DnsQuestion question) {
return query(nextNameServerAddress(), question); return query(nextNameServerAddress(), question);
} }
@ -764,48 +751,39 @@ class DnsNameResolver extends InetNameResolver {
* Sends a DNS query with the specified question using the specified name server list. * Sends a DNS query with the specified question using the specified name server list.
*/ */
public public
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(InetSocketAddress nameServerAddr, DnsQuestion question) { Future<DnsResponse> query(InetSocketAddress nameServerAddr, DnsQuestion question) {
return query0(nameServerAddr, return query0(nameServerAddr,
question, question,
ch.eventLoop().<AddressedEnvelope<DnsResponse, InetSocketAddress>>newPromise()); ch.eventLoop().<DnsResponse>newPromise());
} }
final final
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(InetSocketAddress nameServerAddr, Future<DnsResponse> query0(InetSocketAddress nameServerAddr,
DnsQuestion question, DnsQuestion question,
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) { Promise<DnsResponse> promise) {
return query0(nameServerAddr, question, ch.newPromise(), promise); return query0(nameServerAddr, question, ch.newPromise(), promise);
} }
final final
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(InetSocketAddress nameServerAddr, Future<DnsResponse> query0(InetSocketAddress nameServerAddr,
DnsQuestion question, DnsQuestion question,
ChannelPromise writePromise, ChannelPromise writePromise,
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) { Promise<DnsResponse> promise) {
assert !writePromise.isVoid(); assert !writePromise.isVoid();
final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> castPromise = cast(checkNotNull(promise, "promise"));
try { try {
new DnsQueryContext(this, nameServerAddr, question, castPromise).query(writePromise); new DnsQueryContext(this, nameServerAddr, question, promise).query(writePromise);
return castPromise; return promise;
} catch (Exception e) { } catch (Exception e) {
return castPromise.setFailure(e); return promise.setFailure(e);
} }
} }
@SuppressWarnings("unchecked")
private static
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> cast(Promise<?> promise) {
return (Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>>) promise;
}
/** /**
* Sends a DNS query with the specified question. * Sends a DNS query with the specified question.
*/ */
public public
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(DnsQuestion question, Future<DnsResponse> query(DnsQuestion question, Promise<DnsResponse> promise) {
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) {
return query(nextNameServerAddress(), question, promise); return query(nextNameServerAddress(), question, promise);
} }
@ -813,10 +791,7 @@ class DnsNameResolver extends InetNameResolver {
* Sends a DNS query with the specified question using the specified name server list. * Sends a DNS query with the specified question using the specified name server list.
*/ */
public public
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(InetSocketAddress nameServerAddr, Future<DnsResponse> query(InetSocketAddress nameServerAddr, DnsQuestion question, Promise<DnsResponse> promise) {
DnsQuestion question,
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) {
return query0(nameServerAddr, question, null, promise); return query0(nameServerAddr, question, null, promise);
} }
} }

View File

@ -22,32 +22,18 @@ import static java.util.Collections.unmodifiableList;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import dorkbox.network.dns.DnsQuestion; import dorkbox.network.dns.DnsQuestion;
import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.DnsResponse;
import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsResponseCode; import dorkbox.network.dns.constants.DnsResponseCode;
import dorkbox.network.dns.constants.DnsSection; import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.records.AAAARecord; import dorkbox.network.dns.records.*;
import dorkbox.network.dns.records.ARecord;
import dorkbox.network.dns.records.CNAMERecord;
import dorkbox.network.dns.records.DnsMessage;
import dorkbox.network.dns.records.DnsRecord;
import dorkbox.network.dns.records.NSRecord;
import dorkbox.network.dns.resolver.addressProvider.DnsServerAddressStream; import dorkbox.network.dns.resolver.addressProvider.DnsServerAddressStream;
import dorkbox.network.dns.resolver.addressProvider.DnsServerAddresses; import dorkbox.network.dns.resolver.addressProvider.DnsServerAddresses;
import dorkbox.network.dns.resolver.cache.DnsCache; import dorkbox.network.dns.resolver.cache.DnsCache;
import dorkbox.network.dns.resolver.cache.DnsCacheEntry; import dorkbox.network.dns.resolver.cache.DnsCacheEntry;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.InternetProtocolFamily; import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
@ -61,10 +47,10 @@ import io.netty.util.internal.ThrowableUtil;
abstract abstract
class DnsNameResolverContext<T> { class DnsNameResolverContext<T> {
private static final FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>> RELEASE_RESPONSE = new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() { private static final FutureListener<DnsResponse> RELEASE_RESPONSE = new FutureListener<DnsResponse>() {
@Override @Override
public public
void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) { void operationComplete(Future<DnsResponse> future) {
if (future.isSuccess()) { if (future.isSuccess()) {
future.getNow() future.getNow()
.release(); .release();
@ -104,7 +90,7 @@ class DnsNameResolverContext<T> {
private final int maxAllowedQueries; private final int maxAllowedQueries;
private final InternetProtocolFamily[] resolvedInternetProtocolFamilies; private final InternetProtocolFamily[] resolvedInternetProtocolFamilies;
private final Set<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> queriesInProgress = Collections.newSetFromMap(new IdentityHashMap<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>, Boolean>()); private final Set<Future<DnsResponse>> queriesInProgress = Collections.newSetFromMap(new IdentityHashMap<Future<DnsResponse>, Boolean>());
private List<DnsCacheEntry> resolvedEntries; private List<DnsCacheEntry> resolvedEntries;
private int allowedQueries; private int allowedQueries;
@ -323,20 +309,20 @@ class DnsNameResolverContext<T> {
final InetSocketAddress nameServerAddr = nameServerAddrStream.next(); final InetSocketAddress nameServerAddr = nameServerAddrStream.next();
final ChannelPromise writePromise = parent.ch.newPromise(); final ChannelPromise writePromise = parent.ch.newPromise();
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = final Future<DnsResponse> f =
parent.query0(nameServerAddr, parent.query0(nameServerAddr,
question, question,
writePromise, writePromise,
parent.ch.eventLoop().<AddressedEnvelope<DnsResponse, InetSocketAddress>>newPromise()); parent.ch.eventLoop().<DnsResponse>newPromise());
queriesInProgress.add(f); queriesInProgress.add(f);
queryLifecycleObserver.queryWritten(nameServerAddr, writePromise); queryLifecycleObserver.queryWritten(nameServerAddr, writePromise);
f.addListener(new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() { f.addListener(new FutureListener<DnsResponse>() {
@Override @Override
public public
void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) { void operationComplete(Future<DnsResponse> future) {
// future.result() should have refCnt=2 // future.result() should have refCnt=2
// question should have refCnt=1 // question should have refCnt=1
queriesInProgress.remove(future); queriesInProgress.remove(future);
@ -346,7 +332,7 @@ class DnsNameResolverContext<T> {
return; return;
} }
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = future.getNow(); DnsResponse envelope = future.getNow();
try { try {
if (future.isSuccess()) { if (future.isSuccess()) {
onResponse(nameServerAddrStream, onResponse(nameServerAddrStream,
@ -378,17 +364,15 @@ class DnsNameResolverContext<T> {
void onResponse(final DnsServerAddressStream nameServerAddrStream, void onResponse(final DnsServerAddressStream nameServerAddrStream,
final int nameServerAddrStreamIndex, final int nameServerAddrStreamIndex,
final DnsQuestion question, final DnsQuestion question, DnsResponse response,
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
Promise<T> promise) { Promise<T> promise) {
final DnsResponse res = envelope.content(); final int code = response.getHeader()
final int code = res.getHeader() .getRcode();
.getRcode();
if (code == DnsResponseCode.NOERROR) { if (code == DnsResponseCode.NOERROR) {
if (handleRedirect(question, envelope, queryLifecycleObserver, promise)) { if (handleRedirect(question, response, queryLifecycleObserver, promise)) {
// Was a redirect so return here as everything else is handled in handleRedirect(...) // Was a redirect so return here as everything else is handled in handleRedirect(...)
return; return;
} }
@ -396,10 +380,10 @@ class DnsNameResolverContext<T> {
.getType(); .getType();
if (type == DnsRecordType.A || type == DnsRecordType.AAAA) { if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
onResponseAorAAAA(type, question, envelope, queryLifecycleObserver, promise); onResponseAorAAAA(type, question, response, queryLifecycleObserver, promise);
} }
else if (type == DnsRecordType.CNAME) { else if (type == DnsRecordType.CNAME) {
onResponseCNAME(question, envelope, queryLifecycleObserver, promise); onResponseCNAME(question, response, queryLifecycleObserver, promise);
} }
else { else {
queryLifecycleObserver.queryFailed(UNRECOGNIZED_TYPE_QUERY_FAILED_EXCEPTION); queryLifecycleObserver.queryFailed(UNRECOGNIZED_TYPE_QUERY_FAILED_EXCEPTION);
@ -424,23 +408,20 @@ class DnsNameResolverContext<T> {
* Handles a redirect answer if needed and returns {@code true} if a redirect query has been made. * Handles a redirect answer if needed and returns {@code true} if a redirect query has been made.
*/ */
private private
boolean handleRedirect(DnsQuestion question, boolean handleRedirect(DnsQuestion question, DnsResponse response,
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
Promise<T> promise) { Promise<T> promise) {
final DnsResponse res = envelope.content();
// Check if we have answers, if not this may be an non authority NS and so redirects must be handled. // Check if we have answers, if not this may be an non authority NS and so redirects must be handled.
DnsRecord[] answerArray = res.getSectionArray(DnsSection.ANSWER); DnsRecord[] answerArray = response.getSectionArray(DnsSection.ANSWER);
if (answerArray.length == 0) { if (answerArray.length == 0) {
AuthoritativeNameServerList serverNames = extractAuthoritativeNameServers(question.getQuestion() AuthoritativeNameServerList serverNames = extractAuthoritativeNameServers(question.getQuestion()
.getName() .getName()
.toString(), res); .toString(), response);
if (serverNames != null) { if (serverNames != null) {
List<InetSocketAddress> nameServers = new ArrayList<InetSocketAddress>(serverNames.size()); List<InetSocketAddress> nameServers = new ArrayList<InetSocketAddress>(serverNames.size());
DnsRecord[] additionalArray = res.getSectionArray(DnsSection.ADDITIONAL); DnsRecord[] additionalArray = response.getSectionArray(DnsSection.ADDITIONAL);
for (int i = 0; i < additionalArray.length; i++) { for (int i = 0; i < additionalArray.length; i++) {
final DnsRecord r = additionalArray[i]; final DnsRecord r = additionalArray[i];
@ -505,13 +486,11 @@ class DnsNameResolverContext<T> {
private private
void onResponseAorAAAA(int qType, void onResponseAorAAAA(int qType,
DnsMessage question, DnsMessage question, DnsResponse response,
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
Promise<T> promise) { Promise<T> promise) {
// We often get a bunch of CNAMES as well when we asked for A/AAAA. // We often get a bunch of CNAMES as well when we asked for A/AAAA.
final DnsResponse response = envelope.content();
final Map<String, String> cnames = buildAliasMap(response); final Map<String, String> cnames = buildAliasMap(response);
DnsRecord[] answerArray = response.getSectionArray(DnsSection.ANSWER); DnsRecord[] answerArray = response.getSectionArray(DnsSection.ANSWER);
@ -573,7 +552,7 @@ class DnsNameResolverContext<T> {
} }
else { else {
// We asked for A/AAAA but we got only CNAME. // We asked for A/AAAA but we got only CNAME.
onResponseCNAME(question, envelope, cnames, queryLifecycleObserver, promise); onResponseCNAME(question, response, cnames, queryLifecycleObserver, promise);
} }
} }
@ -595,16 +574,14 @@ class DnsNameResolverContext<T> {
} }
private private
void onResponseCNAME(DnsMessage question, void onResponseCNAME(DnsMessage question, DnsResponse response,
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
Promise<T> promise) { Promise<T> promise) {
onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), queryLifecycleObserver, promise); onResponseCNAME(question, response, buildAliasMap(response), queryLifecycleObserver, promise);
} }
private private
void onResponseCNAME(DnsMessage question, void onResponseCNAME(DnsMessage question, DnsResponse response,
AddressedEnvelope<DnsResponse, InetSocketAddress> response,
Map<String, String> cnames, Map<String, String> cnames,
final DnsQueryLifecycleObserver queryLifecycleObserver, final DnsQueryLifecycleObserver queryLifecycleObserver,
Promise<T> promise) { Promise<T> promise) {
@ -748,8 +725,8 @@ class DnsNameResolverContext<T> {
if (!queriesInProgress.isEmpty()) { if (!queriesInProgress.isEmpty()) {
// If there are queries in progress, we should cancel it because we already finished the resolution. // If there are queries in progress, we should cancel it because we already finished the resolution.
for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator(); i.hasNext(); ) { for (Iterator<Future<DnsResponse>> i = queriesInProgress.iterator(); i.hasNext(); ) {
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = i.next(); Future<DnsResponse> f = i.next();
i.remove(); i.remove();
if (!f.cancel(false)) { if (!f.cancel(false)) {

View File

@ -24,7 +24,6 @@ import dorkbox.network.dns.DnsQuestion;
import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.DnsResponse;
import dorkbox.network.dns.constants.DnsSection; import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.records.DnsRecord; import dorkbox.network.dns.records.DnsRecord;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
@ -42,7 +41,7 @@ class DnsQueryContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class);
private final DnsNameResolver parent; private final DnsNameResolver parent;
private final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise; private final Promise<DnsResponse> promise;
private final int id; private final int id;
private final DnsQuestion question; private final DnsQuestion question;
@ -53,7 +52,7 @@ class DnsQueryContext {
DnsQueryContext(DnsNameResolver parent, DnsQueryContext(DnsNameResolver parent,
InetSocketAddress nameServerAddr, InetSocketAddress nameServerAddr,
DnsQuestion question, DnsQuestion question,
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) { Promise<DnsResponse> promise) {
this.parent = checkNotNull(parent, "parent"); this.parent = checkNotNull(parent, "parent");
this.nameServerAddr = checkNotNull(nameServerAddr, "nameServerAddr"); this.nameServerAddr = checkNotNull(nameServerAddr, "nameServerAddr");
@ -175,29 +174,28 @@ class DnsQueryContext {
promise.tryFailure(e); promise.tryFailure(e);
} }
void finish(AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) { void finish(DnsResponse response) {
final DnsResponse response = envelope.content();
try { try {
DnsRecord[] sectionArray = response.getSectionArray(DnsSection.QUESTION); DnsRecord[] sectionArray = response.getSectionArray(DnsSection.QUESTION);
if (sectionArray.length != 1) { if (sectionArray.length != 1) {
logger.warn("Received a DNS response with invalid number of questions: {}", envelope); logger.warn("Received a DNS response with invalid number of questions: {}", response);
return; return;
} }
DnsRecord[] questionArray = question.getSectionArray(DnsSection.QUESTION); DnsRecord[] questionArray = question.getSectionArray(DnsSection.QUESTION);
if (questionArray.length != 1) { if (questionArray.length != 1) {
logger.warn("Received a DNS response with invalid number of query questions: {}", envelope); logger.warn("Received a DNS response with invalid number of query questions: {}", response);
return; return;
} }
if (!questionArray[0].equals(sectionArray[0])) { if (!questionArray[0].equals(sectionArray[0])) {
logger.warn("Received a mismatching DNS response: {}", envelope); logger.warn("Received a mismatching DNS response: {}", response);
return; return;
} }
setSuccess(envelope); setSuccess(response);
} finally { } finally {
if (question.isResolveQuestion()) { if (question.isResolveQuestion()) {
// for resolve questions (always A/AAAA), we convert the answer into InetAddress, however with OTHER TYPES, we pass // for resolve questions (always A/AAAA), we convert the answer into InetAddress, however with OTHER TYPES, we pass
@ -208,7 +206,7 @@ class DnsQueryContext {
} }
private private
void setSuccess(AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) { void setSuccess(DnsResponse response) {
parent.queryContextManager.remove(nameServerAddr(), id); parent.queryContextManager.remove(nameServerAddr(), id);
// Cancel the timeout task. // Cancel the timeout task.
@ -217,17 +215,16 @@ class DnsQueryContext {
timeoutFuture.cancel(false); timeoutFuture.cancel(false);
} }
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise = this.promise; Promise<DnsResponse> promise = this.promise;
if (promise.setUncancellable()) { if (promise.setUncancellable()) {
@SuppressWarnings("unchecked") response.retain();
AddressedEnvelope<DnsResponse, InetSocketAddress> castResponse = envelope.retain(); // response now has a refCnt = 2
// envelope now has a refCnt = 2 if (!promise.trySuccess(response)) { // question is used here!
if (!promise.trySuccess(castResponse)) { // question is used here!
// We failed to notify the promise as it was failed before, thus we need to release the envelope // We failed to notify the promise as it was failed before, thus we need to release the envelope
envelope.release(); response.release();
} }
envelope.release(); response.release();
} }
} }
} }