forked from netty/netty
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Motivation: There are various known issues in netty-codec-dns: - Message types are not interfaces, which can make it difficult for a user to implement his/her own message implementation. - Some class names and field names do not match with the terms in the RFC. - The support for decoding a DNS record was limited. A user had to encode and decode by him/herself. - The separation of DnsHeader from DnsMessage was unnecessary, although it is fine conceptually. - Buffer leak caused by DnsMessage was difficult to analyze, because the leak detector tracks down the underlying ByteBuf rather than the DnsMessage itself. - DnsMessage assumes DNS-over-UDP. - To send an EDNS message, a user have to create a new DNS record class instance unnecessarily. Modifications: - Make all message types interfaces and add default implementations - Rename some classes, properties, and constants to match the RFCs - DnsResource -> DnsRecord - DnsType -> DnsRecordType - and many more - Remove DnsClass and use an integer to support EDNS better - Add DnsRecordEncoder/DnsRecordDecoder and their default implementations - DnsRecord does not require RDATA to be ByteBuf anymore. - Add DnsRawRecord as the catch-all record type - Merge DnsHeader into DnsMessage - Make ResourceLeakDetector track AbstractDnsMessage - Remove DnsMessage.sender/recipient properties - Wrap DnsMessage with AddressedEnvelope - Add DatagramDnsQuest and DatagramDnsResponse for ease of use - Rename DnsQueryEncoder to DatagramDnsQueryEncoder - Rename DnsResponseDecoder to DatagramDnsResponseDecoder - Miscellaneous changes - Add StringUtil.TAB Result: - Cleaner APi - Can support DNS-over-TCP more easily in the future - Reduced memory footprint in the default DnsQuery/Response implementations - Better leak tracking for DnsMessages - Possibility to introduce new DnsRecord types in the future and provide full record encoder/decoder implementation. - No unnecessary instantiation for an EDNS pseudo resource record
- Loading branch information
Showing
44 changed files
with
3,126 additions
and
1,854 deletions.
There are no files selected for viewing
465 changes: 465 additions & 0 deletions
465
codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsMessage.java
Large diffs are not rendered by default.
Oops, something went wrong.
139 changes: 139 additions & 0 deletions
139
codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* | ||
* Copyright 2015 The Netty Project | ||
* | ||
* The Netty Project licenses this file to you 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 io.netty.handler.codec.dns; | ||
|
||
import io.netty.util.internal.StringUtil; | ||
|
||
import static io.netty.util.internal.ObjectUtil.checkNotNull; | ||
|
||
/** | ||
* A skeletal implementation of {@link DnsRecord}. | ||
*/ | ||
public abstract class AbstractDnsRecord implements DnsRecord { | ||
|
||
private final String name; | ||
private final DnsRecordType type; | ||
private final short dnsClass; | ||
private final long timeToLive; | ||
private int hashCode; | ||
|
||
/** | ||
* Creates a new {@link #CLASS_IN IN-class} record. | ||
* | ||
* @param name the domain name | ||
* @param type the type of the record | ||
* @param timeToLive the TTL value of the record | ||
*/ | ||
protected AbstractDnsRecord(String name, DnsRecordType type, long timeToLive) { | ||
this(name, type, CLASS_IN, timeToLive); | ||
} | ||
|
||
/** | ||
* Creates a new record. | ||
* | ||
* @param name the domain name | ||
* @param type the type of the record | ||
* @param dnsClass the class of the record, usually one of the following: | ||
* <ul> | ||
* <li>{@link #CLASS_IN}</li> | ||
* <li>{@link #CLASS_CSNET}</li> | ||
* <li>{@link #CLASS_CHAOS}</li> | ||
* <li>{@link #CLASS_HESIOD}</li> | ||
* <li>{@link #CLASS_NONE}</li> | ||
* <li>{@link #CLASS_ANY}</li> | ||
* </ul> | ||
* @param timeToLive the TTL value of the record | ||
*/ | ||
protected AbstractDnsRecord(String name, DnsRecordType type, int dnsClass, long timeToLive) { | ||
if (timeToLive < 0) { | ||
throw new IllegalArgumentException("timeToLive: " + timeToLive + " (expected: >= 0)"); | ||
} | ||
this.name = checkNotNull(name, "name"); | ||
this.type = checkNotNull(type, "type"); | ||
this.dnsClass = (short) dnsClass; | ||
this.timeToLive = timeToLive; | ||
} | ||
|
||
@Override | ||
public String name() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public DnsRecordType type() { | ||
return type; | ||
} | ||
|
||
@Override | ||
public int dnsClass() { | ||
return dnsClass & 0xFFFF; | ||
} | ||
|
||
@Override | ||
public long timeToLive() { | ||
return timeToLive; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (!(obj instanceof DnsRecord)) { | ||
return false; | ||
} | ||
|
||
final DnsRecord that = (DnsRecord) obj; | ||
final int hashCode = this.hashCode; | ||
if (hashCode != 0 && hashCode != that.hashCode()) { | ||
return false; | ||
} | ||
|
||
return type().intValue() == that.type().intValue() && | ||
dnsClass() == that.dnsClass() && | ||
name().equals(that.name()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
final int hashCode = this.hashCode; | ||
if (hashCode != 0) { | ||
return hashCode; | ||
} | ||
|
||
return this.hashCode = name.hashCode() * 31 + type().intValue() * 31 + dnsClass(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
StringBuilder buf = new StringBuilder(64); | ||
|
||
buf.append(StringUtil.simpleClassName(this)) | ||
.append('(') | ||
.append(name()) | ||
.append(' ') | ||
.append(timeToLive()) | ||
.append(' '); | ||
|
||
DnsMessageUtil.appendRecordClass(buf, dnsClass()) | ||
.append(' ') | ||
.append(type().name()) | ||
.append(')'); | ||
|
||
return buf.toString(); | ||
} | ||
} |
190 changes: 190 additions & 0 deletions
190
codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQuery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
/* | ||
* Copyright 2015 The Netty Project | ||
* | ||
* The Netty Project licenses this file to you 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 io.netty.handler.codec.dns; | ||
|
||
import io.netty.channel.AddressedEnvelope; | ||
|
||
import java.net.InetSocketAddress; | ||
import java.net.SocketAddress; | ||
|
||
/** | ||
* A {@link DnsQuery} implementation for UDP/IP. | ||
*/ | ||
public class DatagramDnsQuery extends DefaultDnsQuery | ||
implements AddressedEnvelope<DatagramDnsQuery, InetSocketAddress> { | ||
|
||
private final InetSocketAddress sender; | ||
private final InetSocketAddress recipient; | ||
|
||
/** | ||
* Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode}. | ||
* | ||
* @param sender the address of the sender | ||
* @param recipient the address of the recipient | ||
* @param id the {@code ID} of the DNS query | ||
*/ | ||
public DatagramDnsQuery( | ||
InetSocketAddress sender, InetSocketAddress recipient, int id) { | ||
this(sender, recipient, id, DnsOpCode.QUERY); | ||
} | ||
|
||
/** | ||
* Creates a new instance. | ||
* | ||
* @param sender the address of the sender | ||
* @param recipient the address of the recipient | ||
* @param id the {@code ID} of the DNS query | ||
* @param opCode the {@code opCode} of the DNS query | ||
*/ | ||
public DatagramDnsQuery( | ||
InetSocketAddress sender, InetSocketAddress recipient, int id, DnsOpCode opCode) { | ||
super(id, opCode); | ||
|
||
if (recipient == null && sender == null) { | ||
throw new NullPointerException("recipient and sender"); | ||
} | ||
|
||
this.sender = sender; | ||
this.recipient = recipient; | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery content() { | ||
return this; | ||
} | ||
|
||
@Override | ||
public InetSocketAddress sender() { | ||
return sender; | ||
} | ||
|
||
@Override | ||
public InetSocketAddress recipient() { | ||
return recipient; | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery setId(int id) { | ||
return (DatagramDnsQuery) super.setId(id); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery setOpCode(DnsOpCode opCode) { | ||
return (DatagramDnsQuery) super.setOpCode(opCode); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery setRecursionDesired(boolean recursionDesired) { | ||
return (DatagramDnsQuery) super.setRecursionDesired(recursionDesired); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery setZ(int z) { | ||
return (DatagramDnsQuery) super.setZ(z); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery setRecord(DnsSection section, DnsRecord record) { | ||
return (DatagramDnsQuery) super.setRecord(section, record); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery addRecord(DnsSection section, DnsRecord record) { | ||
return (DatagramDnsQuery) super.addRecord(section, record); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery addRecord(DnsSection section, int index, DnsRecord record) { | ||
return (DatagramDnsQuery) super.addRecord(section, index, record); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery clear(DnsSection section) { | ||
return (DatagramDnsQuery) super.clear(section); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery clear() { | ||
return (DatagramDnsQuery) super.clear(); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery touch() { | ||
return (DatagramDnsQuery) super.touch(); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery touch(Object hint) { | ||
return (DatagramDnsQuery) super.touch(hint); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery retain() { | ||
return (DatagramDnsQuery) super.retain(); | ||
} | ||
|
||
@Override | ||
public DatagramDnsQuery retain(int increment) { | ||
return (DatagramDnsQuery) super.retain(increment); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (!super.equals(obj)) { | ||
return false; | ||
} | ||
|
||
if (!(obj instanceof AddressedEnvelope)) { | ||
return false; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
final AddressedEnvelope<?, SocketAddress> that = (AddressedEnvelope<?, SocketAddress>) obj; | ||
if (sender() == null) { | ||
if (that.sender() != null) { | ||
return false; | ||
} | ||
} else if (!sender().equals(that.sender())) { | ||
return false; | ||
} | ||
|
||
if (recipient() == null) { | ||
if (that.recipient() != null) { | ||
return false; | ||
} | ||
} else if (!recipient().equals(that.recipient())) { | ||
return false; | ||
} | ||
|
||
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; | ||
} | ||
} |
Oops, something went wrong.