diff --git a/src/dorkbox/network/dns/DnsEnvelope.java b/src/dorkbox/network/dns/DnsEnvelope.java new file mode 100644 index 00000000..d1a49ed6 --- /dev/null +++ b/src/dorkbox/network/dns/DnsEnvelope.java @@ -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 { + + 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); + } +} diff --git a/src/dorkbox/network/dns/DnsQuestion.java b/src/dorkbox/network/dns/DnsQuestion.java index 95899426..91b2bc05 100644 --- a/src/dorkbox/network/dns/DnsQuestion.java +++ b/src/dorkbox/network/dns/DnsQuestion.java @@ -6,12 +6,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Locale; -import dorkbox.network.dns.constants.DnsClass; -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.constants.*; import dorkbox.network.dns.records.DnsRecord; import io.netty.channel.AddressedEnvelope; import io.netty.util.internal.StringUtil; @@ -20,29 +15,7 @@ import io.netty.util.internal.StringUtil; * */ public -class DnsQuestion extends DnsMessage implements AddressedEnvelope { - 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; - } - - - +class DnsQuestion extends DnsEnvelope { public static DnsQuestion newResolveQuestion(final String inetHost, final int type, final boolean isRecursionDesired) { return newQuestion(inetHost, type, isRecursionDesired, true); @@ -53,7 +26,6 @@ class DnsQuestion extends DnsMessage implements AddressedEnvelope { - - private final InetSocketAddress sender; - private final InetSocketAddress recipient; +public +class DnsResponse extends DnsEnvelope { /** * Creates a new instance. * - * @param sender the address of the sender - * @param recipient the address of the recipient + * @param localAddress the address of the sender + * @param remoteAddress the address of the recipient */ public - DnsResponse(InetSocketAddress sender, InetSocketAddress recipient, final DnsInput dnsInput) throws IOException { - super(dnsInput); + DnsResponse(final DnsInput dnsInput, InetSocketAddress localAddress, InetSocketAddress remoteAddress) throws IOException { + super(dnsInput, localAddress, remoteAddress); - if (recipient == null && sender == null) { - throw new NullPointerException("recipient and sender"); + if (remoteAddress == null && localAddress == null) { + throw new NullPointerException("localAddress and remoteAddress"); } - - this.sender = sender; - this.recipient = recipient; } @Override public - DnsResponse content() { - return this; - } - - @Override - public InetSocketAddress sender() { - return sender; - } - - @Override - public InetSocketAddress recipient() { - return recipient; - } - - - - - @Override - public - DnsResponse touch() { - return (DnsResponse) super.touch(); + int hashCode() { + int hashCode = super.hashCode(); + if (sender() != null) { + hashCode = hashCode * 31 + sender().hashCode(); + } + if (recipient() != null) { + hashCode = hashCode * 31 + recipient().hashCode(); + } + return hashCode; } @Override public - DnsResponse touch(Object hint) { - 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) { + boolean equals(Object obj) { if (this == obj) { return true; } @@ -113,7 +78,8 @@ public class DnsResponse extends DnsMessage implements AddressedEnvelope> query(DnsQuestion question) { + Future query(DnsQuestion 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. */ public - Future> query(InetSocketAddress nameServerAddr, DnsQuestion question) { + Future query(InetSocketAddress nameServerAddr, DnsQuestion question) { return query0(nameServerAddr, question, - ch.eventLoop().>newPromise()); + ch.eventLoop().newPromise()); } final - Future> query0(InetSocketAddress nameServerAddr, + Future query0(InetSocketAddress nameServerAddr, DnsQuestion question, - Promise> promise) { + Promise promise) { return query0(nameServerAddr, question, ch.newPromise(), promise); } final - Future> query0(InetSocketAddress nameServerAddr, - DnsQuestion question, - ChannelPromise writePromise, - Promise> promise) { + Future query0(InetSocketAddress nameServerAddr, + DnsQuestion question, + ChannelPromise writePromise, + Promise promise) { assert !writePromise.isVoid(); - final Promise> castPromise = cast(checkNotNull(promise, "promise")); try { - new DnsQueryContext(this, nameServerAddr, question, castPromise).query(writePromise); - return castPromise; + new DnsQueryContext(this, nameServerAddr, question, promise).query(writePromise); + return promise; } catch (Exception e) { - return castPromise.setFailure(e); + return promise.setFailure(e); } } - @SuppressWarnings("unchecked") - private static - Promise> cast(Promise promise) { - return (Promise>) promise; - } - - /** * Sends a DNS query with the specified question. */ public - Future> query(DnsQuestion question, - Promise> promise) { + Future query(DnsQuestion question, Promise 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. */ public - Future> query(InetSocketAddress nameServerAddr, - DnsQuestion question, - Promise> promise) { - + Future query(InetSocketAddress nameServerAddr, DnsQuestion question, Promise promise) { return query0(nameServerAddr, question, null, promise); } } diff --git a/src/dorkbox/network/dns/resolver/DnsNameResolverContext.java b/src/dorkbox/network/dns/resolver/DnsNameResolverContext.java index e9e8697c..afb527f6 100644 --- a/src/dorkbox/network/dns/resolver/DnsNameResolverContext.java +++ b/src/dorkbox/network/dns/resolver/DnsNameResolverContext.java @@ -22,32 +22,18 @@ import static java.util.Collections.unmodifiableList; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; -import java.util.ArrayList; -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 java.util.*; import dorkbox.network.dns.DnsQuestion; import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.constants.DnsResponseCode; import dorkbox.network.dns.constants.DnsSection; -import dorkbox.network.dns.records.AAAARecord; -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.records.*; import dorkbox.network.dns.resolver.addressProvider.DnsServerAddressStream; import dorkbox.network.dns.resolver.addressProvider.DnsServerAddresses; import dorkbox.network.dns.resolver.cache.DnsCache; import dorkbox.network.dns.resolver.cache.DnsCacheEntry; -import io.netty.channel.AddressedEnvelope; import io.netty.channel.ChannelPromise; import io.netty.channel.socket.InternetProtocolFamily; import io.netty.util.concurrent.Future; @@ -61,10 +47,10 @@ import io.netty.util.internal.ThrowableUtil; abstract class DnsNameResolverContext { - private static final FutureListener> RELEASE_RESPONSE = new FutureListener>() { + private static final FutureListener RELEASE_RESPONSE = new FutureListener() { @Override public - void operationComplete(Future> future) { + void operationComplete(Future future) { if (future.isSuccess()) { future.getNow() .release(); @@ -104,7 +90,7 @@ class DnsNameResolverContext { private final int maxAllowedQueries; private final InternetProtocolFamily[] resolvedInternetProtocolFamilies; - private final Set>> queriesInProgress = Collections.newSetFromMap(new IdentityHashMap>, Boolean>()); + private final Set> queriesInProgress = Collections.newSetFromMap(new IdentityHashMap, Boolean>()); private List resolvedEntries; private int allowedQueries; @@ -323,20 +309,20 @@ class DnsNameResolverContext { final InetSocketAddress nameServerAddr = nameServerAddrStream.next(); final ChannelPromise writePromise = parent.ch.newPromise(); - final Future> f = + final Future f = parent.query0(nameServerAddr, question, writePromise, - parent.ch.eventLoop().>newPromise()); + parent.ch.eventLoop().newPromise()); queriesInProgress.add(f); queryLifecycleObserver.queryWritten(nameServerAddr, writePromise); - f.addListener(new FutureListener>() { + f.addListener(new FutureListener() { @Override public - void operationComplete(Future> future) { + void operationComplete(Future future) { // future.result() should have refCnt=2 // question should have refCnt=1 queriesInProgress.remove(future); @@ -346,7 +332,7 @@ class DnsNameResolverContext { return; } - AddressedEnvelope envelope = future.getNow(); + DnsResponse envelope = future.getNow(); try { if (future.isSuccess()) { onResponse(nameServerAddrStream, @@ -378,17 +364,15 @@ class DnsNameResolverContext { void onResponse(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex, - final DnsQuestion question, - AddressedEnvelope envelope, + final DnsQuestion question, DnsResponse response, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise promise) { - final DnsResponse res = envelope.content(); - final int code = res.getHeader() - .getRcode(); + final int code = response.getHeader() + .getRcode(); 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(...) return; } @@ -396,10 +380,10 @@ class DnsNameResolverContext { .getType(); if (type == DnsRecordType.A || type == DnsRecordType.AAAA) { - onResponseAorAAAA(type, question, envelope, queryLifecycleObserver, promise); + onResponseAorAAAA(type, question, response, queryLifecycleObserver, promise); } else if (type == DnsRecordType.CNAME) { - onResponseCNAME(question, envelope, queryLifecycleObserver, promise); + onResponseCNAME(question, response, queryLifecycleObserver, promise); } else { queryLifecycleObserver.queryFailed(UNRECOGNIZED_TYPE_QUERY_FAILED_EXCEPTION); @@ -424,23 +408,20 @@ class DnsNameResolverContext { * Handles a redirect answer if needed and returns {@code true} if a redirect query has been made. */ private - boolean handleRedirect(DnsQuestion question, - AddressedEnvelope envelope, + boolean handleRedirect(DnsQuestion question, DnsResponse response, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise 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. - DnsRecord[] answerArray = res.getSectionArray(DnsSection.ANSWER); + DnsRecord[] answerArray = response.getSectionArray(DnsSection.ANSWER); if (answerArray.length == 0) { AuthoritativeNameServerList serverNames = extractAuthoritativeNameServers(question.getQuestion() .getName() - .toString(), res); + .toString(), response); if (serverNames != null) { List nameServers = new ArrayList(serverNames.size()); - DnsRecord[] additionalArray = res.getSectionArray(DnsSection.ADDITIONAL); + DnsRecord[] additionalArray = response.getSectionArray(DnsSection.ADDITIONAL); for (int i = 0; i < additionalArray.length; i++) { final DnsRecord r = additionalArray[i]; @@ -505,13 +486,11 @@ class DnsNameResolverContext { private void onResponseAorAAAA(int qType, - DnsMessage question, - AddressedEnvelope envelope, + DnsMessage question, DnsResponse response, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise promise) { // We often get a bunch of CNAMES as well when we asked for A/AAAA. - final DnsResponse response = envelope.content(); final Map cnames = buildAliasMap(response); DnsRecord[] answerArray = response.getSectionArray(DnsSection.ANSWER); @@ -573,7 +552,7 @@ class DnsNameResolverContext { } else { // 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 { } private - void onResponseCNAME(DnsMessage question, - AddressedEnvelope envelope, + void onResponseCNAME(DnsMessage question, DnsResponse response, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise promise) { - onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), queryLifecycleObserver, promise); + onResponseCNAME(question, response, buildAliasMap(response), queryLifecycleObserver, promise); } private - void onResponseCNAME(DnsMessage question, - AddressedEnvelope response, + void onResponseCNAME(DnsMessage question, DnsResponse response, Map cnames, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise promise) { @@ -748,8 +725,8 @@ class DnsNameResolverContext { if (!queriesInProgress.isEmpty()) { // If there are queries in progress, we should cancel it because we already finished the resolution. - for (Iterator>> i = queriesInProgress.iterator(); i.hasNext(); ) { - Future> f = i.next(); + for (Iterator> i = queriesInProgress.iterator(); i.hasNext(); ) { + Future f = i.next(); i.remove(); if (!f.cancel(false)) { diff --git a/src/dorkbox/network/dns/resolver/DnsQueryContext.java b/src/dorkbox/network/dns/resolver/DnsQueryContext.java index c22c6869..8a52cc08 100644 --- a/src/dorkbox/network/dns/resolver/DnsQueryContext.java +++ b/src/dorkbox/network/dns/resolver/DnsQueryContext.java @@ -24,7 +24,6 @@ import dorkbox.network.dns.DnsQuestion; import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.constants.DnsSection; import dorkbox.network.dns.records.DnsRecord; -import io.netty.channel.AddressedEnvelope; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -42,7 +41,7 @@ class DnsQueryContext { private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class); private final DnsNameResolver parent; - private final Promise> promise; + private final Promise promise; private final int id; private final DnsQuestion question; @@ -53,7 +52,7 @@ class DnsQueryContext { DnsQueryContext(DnsNameResolver parent, InetSocketAddress nameServerAddr, DnsQuestion question, - Promise> promise) { + Promise promise) { this.parent = checkNotNull(parent, "parent"); this.nameServerAddr = checkNotNull(nameServerAddr, "nameServerAddr"); @@ -175,29 +174,28 @@ class DnsQueryContext { promise.tryFailure(e); } - void finish(AddressedEnvelope envelope) { - final DnsResponse response = envelope.content(); + void finish(DnsResponse response) { try { DnsRecord[] sectionArray = response.getSectionArray(DnsSection.QUESTION); 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; } DnsRecord[] questionArray = question.getSectionArray(DnsSection.QUESTION); 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; } if (!questionArray[0].equals(sectionArray[0])) { - logger.warn("Received a mismatching DNS response: {}", envelope); + logger.warn("Received a mismatching DNS response: {}", response); return; } - setSuccess(envelope); + setSuccess(response); } finally { if (question.isResolveQuestion()) { // 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 - void setSuccess(AddressedEnvelope envelope) { + void setSuccess(DnsResponse response) { parent.queryContextManager.remove(nameServerAddr(), id); // Cancel the timeout task. @@ -217,17 +215,16 @@ class DnsQueryContext { timeoutFuture.cancel(false); } - Promise> promise = this.promise; + Promise promise = this.promise; if (promise.setUncancellable()) { - @SuppressWarnings("unchecked") - AddressedEnvelope castResponse = envelope.retain(); - // envelope now has a refCnt = 2 - if (!promise.trySuccess(castResponse)) { // question is used here! + response.retain(); + // response now has a refCnt = 2 + if (!promise.trySuccess(response)) { // question is used here! // 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(); } } }