Revamp DNS codec
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
This commit is contained in:
parent
8271c8afcc
commit
63a02fc04e
@ -0,0 +1,465 @@
|
||||
/*
|
||||
* 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.AbstractReferenceCounted;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
import io.netty.util.ResourceLeak;
|
||||
import io.netty.util.ResourceLeakDetector;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* A skeletal implementation of {@link DnsMessage}.
|
||||
*/
|
||||
public abstract class AbstractDnsMessage extends AbstractReferenceCounted implements DnsMessage {
|
||||
|
||||
private static final ResourceLeakDetector<DnsMessage> leakDetector =
|
||||
new ResourceLeakDetector<DnsMessage>(DnsMessage.class);
|
||||
|
||||
private static final int SECTION_QUESTION = DnsSection.QUESTION.ordinal();
|
||||
private static final int SECTION_COUNT = 4;
|
||||
|
||||
private final ResourceLeak leak = leakDetector.open(this);
|
||||
private short id;
|
||||
private DnsOpCode opCode;
|
||||
private boolean recursionDesired;
|
||||
private byte z;
|
||||
|
||||
// To reduce the memory footprint of a message,
|
||||
// each of the following fields is a single record or a list of records.
|
||||
private Object questions;
|
||||
private Object answers;
|
||||
private Object authorities;
|
||||
private Object additionals;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified {@code id} and {@link DnsOpCode#QUERY} opCode.
|
||||
*/
|
||||
protected AbstractDnsMessage(int id) {
|
||||
this(id, DnsOpCode.QUERY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified {@code id} and {@code opCode}.
|
||||
*/
|
||||
protected AbstractDnsMessage(int id, DnsOpCode opCode) {
|
||||
setId(id);
|
||||
setOpCode(opCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int id() {
|
||||
return id & 0xFFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage setId(int id) {
|
||||
this.id = (short) id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsOpCode opCode() {
|
||||
return opCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage setOpCode(DnsOpCode opCode) {
|
||||
this.opCode = checkNotNull(opCode, "opCode");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecursionDesired() {
|
||||
return recursionDesired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage setRecursionDesired(boolean recursionDesired) {
|
||||
this.recursionDesired = recursionDesired;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int z() {
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage setZ(int z) {
|
||||
this.z = (byte) (z & 7);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count(DnsSection section) {
|
||||
return count(sectionOrdinal(section));
|
||||
}
|
||||
|
||||
private int count(int section) {
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
return 0;
|
||||
}
|
||||
if (records instanceof DnsRecord) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
return recordList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < SECTION_COUNT; i ++) {
|
||||
count += count(i);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends DnsRecord> T recordAt(DnsSection section) {
|
||||
return recordAt(sectionOrdinal(section));
|
||||
}
|
||||
|
||||
private <T extends DnsRecord> T recordAt(int section) {
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
return castRecord(records);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
if (recordList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return castRecord(recordList.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends DnsRecord> T recordAt(DnsSection section, int index) {
|
||||
return recordAt(sectionOrdinal(section), index);
|
||||
}
|
||||
|
||||
private <T extends DnsRecord> T recordAt(int section, int index) {
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: none)");
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
if (index == 0) {
|
||||
return castRecord(records);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("index: " + index + "' (expected: 0)");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
return castRecord(recordList.get(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage setRecord(DnsSection section, DnsRecord record) {
|
||||
setRecord(sectionOrdinal(section), record);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void setRecord(int section, DnsRecord record) {
|
||||
clear(section);
|
||||
setSection(section, checkQuestion(section, record));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends DnsRecord> T setRecord(DnsSection section, int index, DnsRecord record) {
|
||||
return setRecord(sectionOrdinal(section), index, record);
|
||||
}
|
||||
|
||||
private <T extends DnsRecord> T setRecord(int section, int index, DnsRecord record) {
|
||||
checkQuestion(section, record);
|
||||
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: none)");
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
if (index == 0) {
|
||||
setSection(section, record);
|
||||
return castRecord(records);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
return castRecord(recordList.set(index, record));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage addRecord(DnsSection section, DnsRecord record) {
|
||||
addRecord(sectionOrdinal(section), record);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addRecord(int section, DnsRecord record) {
|
||||
checkQuestion(section, record);
|
||||
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
setSection(section, record);
|
||||
return;
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
final List<DnsRecord> recordList = newRecordList();
|
||||
recordList.add(castRecord(records));
|
||||
recordList.add(record);
|
||||
setSection(section, recordList);
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
recordList.add(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage addRecord(DnsSection section, int index, DnsRecord record) {
|
||||
addRecord(sectionOrdinal(section), index, record);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addRecord(int section, int index, DnsRecord record) {
|
||||
checkQuestion(section, record);
|
||||
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
if (index != 0) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)");
|
||||
}
|
||||
|
||||
setSection(section, record);
|
||||
return;
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
final List<DnsRecord> recordList;
|
||||
if (index == 0) {
|
||||
recordList = newRecordList();
|
||||
recordList.add(record);
|
||||
recordList.add(castRecord(records));
|
||||
} else if (index == 1) {
|
||||
recordList = newRecordList();
|
||||
recordList.add(castRecord(records));
|
||||
recordList.add(record);
|
||||
} else {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: 0 or 1)");
|
||||
}
|
||||
setSection(section, recordList);
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
recordList.add(index, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends DnsRecord> T removeRecord(DnsSection section, int index) {
|
||||
return removeRecord(sectionOrdinal(section), index);
|
||||
}
|
||||
|
||||
private <T extends DnsRecord> T removeRecord(int section, int index) {
|
||||
final Object records = sectionAt(section);
|
||||
if (records == null) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: none)");
|
||||
}
|
||||
|
||||
if (records instanceof DnsRecord) {
|
||||
if (index != 0) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)");
|
||||
}
|
||||
|
||||
T record = castRecord(records);
|
||||
setSection(section, null);
|
||||
return record;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<DnsRecord> recordList = (List<DnsRecord>) records;
|
||||
return castRecord(recordList.remove(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage clear(DnsSection section) {
|
||||
clear(sectionOrdinal(section));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage clear() {
|
||||
for (int i = 0; i < SECTION_COUNT; i ++) {
|
||||
clear(i);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void clear(int section) {
|
||||
final Object recordOrList = sectionAt(section);
|
||||
setSection(section, null);
|
||||
if (recordOrList instanceof ReferenceCounted) {
|
||||
((ReferenceCounted) recordOrList).release();
|
||||
} else if (recordOrList instanceof List) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<DnsRecord> list = (List<DnsRecord>) recordOrList;
|
||||
if (!list.isEmpty()) {
|
||||
for (Object r : list) {
|
||||
ReferenceCountUtil.release(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage touch() {
|
||||
return (DnsMessage) super.touch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage touch(Object hint) {
|
||||
if (leak != null) {
|
||||
leak.record(hint);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage retain() {
|
||||
return (DnsMessage) super.retain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage retain(int increment) {
|
||||
return (DnsMessage) super.retain(increment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deallocate() {
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof DnsMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final DnsMessage that = (DnsMessage) obj;
|
||||
if (id() != that.id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this instanceof DnsQuery) {
|
||||
if (!(that instanceof DnsQuery)) {
|
||||
return false;
|
||||
}
|
||||
} else if (that instanceof DnsQuery) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id() * 31 + (this instanceof DnsQuery? 0 : 1);
|
||||
}
|
||||
|
||||
private Object sectionAt(int section) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return questions;
|
||||
case 1:
|
||||
return answers;
|
||||
case 2:
|
||||
return authorities;
|
||||
case 3:
|
||||
return additionals;
|
||||
}
|
||||
|
||||
throw new Error(); // Should never reach here.
|
||||
}
|
||||
|
||||
private void setSection(int section, Object value) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
questions = value;
|
||||
return;
|
||||
case 1:
|
||||
answers = value;
|
||||
return;
|
||||
case 2:
|
||||
authorities = value;
|
||||
return;
|
||||
case 3:
|
||||
additionals = value;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(); // Should never reach here.
|
||||
}
|
||||
|
||||
private static int sectionOrdinal(DnsSection section) {
|
||||
return checkNotNull(section, "section").ordinal();
|
||||
}
|
||||
|
||||
private static DnsRecord checkQuestion(int section, DnsRecord record) {
|
||||
if (section == SECTION_QUESTION && !(checkNotNull(record, "record") instanceof DnsQuestion)) {
|
||||
throw new IllegalArgumentException(
|
||||
"record: " + record + " (expected: " + StringUtil.simpleClassName(DnsQuestion.class) + ')');
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends DnsRecord> T castRecord(Object record) {
|
||||
return (T) record;
|
||||
}
|
||||
|
||||
private static ArrayList<DnsRecord> newRecordList() {
|
||||
return new ArrayList<DnsRecord>(2);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* Encodes a {@link DatagramDnsQuery} (or an {@link AddressedEnvelope} of {@link DnsQuery}} into a
|
||||
* {@link DatagramPacket}.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class DatagramDnsQueryEncoder extends MessageToMessageEncoder<AddressedEnvelope<DnsQuery, InetSocketAddress>> {
|
||||
|
||||
private final DnsRecordEncoder recordEncoder;
|
||||
|
||||
/**
|
||||
* Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}.
|
||||
*/
|
||||
public DatagramDnsQueryEncoder() {
|
||||
this(DnsRecordEncoder.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new encoder with the specified {@code recordEncoder}.
|
||||
*/
|
||||
public DatagramDnsQueryEncoder(DnsRecordEncoder recordEncoder) {
|
||||
this.recordEncoder = checkNotNull(recordEncoder, "recordEncoder");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(
|
||||
ChannelHandlerContext ctx,
|
||||
AddressedEnvelope<DnsQuery, InetSocketAddress> in, List<Object> out) throws Exception {
|
||||
|
||||
final InetSocketAddress recipient = in.recipient();
|
||||
final DnsQuery query = in.content();
|
||||
final ByteBuf buf = allocateBuffer(ctx, in);
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
encodeHeader(query, buf);
|
||||
encodeQuestions(query, buf);
|
||||
encodeRecords(query, DnsSection.ADDITIONAL, buf);
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
out.add(new DatagramPacket(buf, recipient, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a {@link ByteBuf} which will be used for constructing a datagram packet.
|
||||
* Sub-classes may override this method to return a {@link ByteBuf} with a perfect matching initial capacity.
|
||||
*/
|
||||
protected ByteBuf allocateBuffer(
|
||||
ChannelHandlerContext ctx,
|
||||
@SuppressWarnings("unused") AddressedEnvelope<DnsQuery, InetSocketAddress> msg) throws Exception {
|
||||
return ctx.alloc().ioBuffer(1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the header that is always 12 bytes long.
|
||||
*
|
||||
* @param query
|
||||
* the query header being encoded
|
||||
* @param buf
|
||||
* the buffer the encoded data should be written to
|
||||
*/
|
||||
private static void encodeHeader(DnsQuery query, ByteBuf buf) {
|
||||
buf.writeShort(query.id());
|
||||
int flags = 0;
|
||||
flags |= (query.opCode().byteValue() & 0xFF) << 14;
|
||||
flags |= query.isRecursionDesired()? 1 << 8 : 0;
|
||||
buf.writeShort(flags);
|
||||
buf.writeShort(query.count(DnsSection.QUESTION));
|
||||
buf.writeShort(0); // answerCount
|
||||
buf.writeShort(0); // authorityResourceCount
|
||||
buf.writeShort(query.count(DnsSection.ADDITIONAL));
|
||||
}
|
||||
|
||||
private void encodeQuestions(DnsQuery query, ByteBuf buf) throws Exception {
|
||||
final int count = query.count(DnsSection.QUESTION);
|
||||
for (int i = 0; i < count; i ++) {
|
||||
recordEncoder.encodeQuestion((DnsQuestion) query.recordAt(DnsSection.QUESTION, i), buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeRecords(DnsQuery query, DnsSection section, ByteBuf buf) throws Exception {
|
||||
final int count = query.count(section);
|
||||
for (int i = 0; i < count; i ++) {
|
||||
recordEncoder.encodeRecord(query.recordAt(section, i), buf);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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 DnsResponse} implementation for UDP/IP.
|
||||
*/
|
||||
public class DatagramDnsResponse extends DefaultDnsResponse
|
||||
implements AddressedEnvelope<DatagramDnsResponse, InetSocketAddress> {
|
||||
|
||||
private final InetSocketAddress sender;
|
||||
private final InetSocketAddress recipient;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode} and
|
||||
* the {@link DnsResponseCode#NOERROR} {@code RCODE}.
|
||||
*
|
||||
* @param sender the address of the sender
|
||||
* @param recipient the address of the recipient
|
||||
* @param id the {@code ID} of the DNS response
|
||||
*/
|
||||
public DatagramDnsResponse(InetSocketAddress sender, InetSocketAddress recipient, int id) {
|
||||
this(sender, recipient, id, DnsOpCode.QUERY, DnsResponseCode.NOERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link DnsResponseCode#NOERROR} responseCode.
|
||||
*
|
||||
* @param sender the address of the sender
|
||||
* @param recipient the address of the recipient
|
||||
* @param id the {@code ID} of the DNS response
|
||||
* @param opCode the {@code opCode} of the DNS response
|
||||
*/
|
||||
public DatagramDnsResponse(InetSocketAddress sender, InetSocketAddress recipient, int id, DnsOpCode opCode) {
|
||||
this(sender, recipient, id, opCode, DnsResponseCode.NOERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 response
|
||||
* @param opCode the {@code opCode} of the DNS response
|
||||
* @param responseCode the {@code RCODE} of the DNS response
|
||||
*/
|
||||
public DatagramDnsResponse(
|
||||
InetSocketAddress sender, InetSocketAddress recipient,
|
||||
int id, DnsOpCode opCode, DnsResponseCode responseCode) {
|
||||
super(id, opCode, responseCode);
|
||||
|
||||
if (recipient == null && sender == null) {
|
||||
throw new NullPointerException("recipient and sender");
|
||||
}
|
||||
|
||||
this.sender = sender;
|
||||
this.recipient = recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse content() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress sender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress recipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer) {
|
||||
return (DatagramDnsResponse) super.setAuthoritativeAnswer(authoritativeAnswer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setTruncated(boolean truncated) {
|
||||
return (DatagramDnsResponse) super.setTruncated(truncated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setRecursionAvailable(boolean recursionAvailable) {
|
||||
return (DatagramDnsResponse) super.setRecursionAvailable(recursionAvailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setCode(DnsResponseCode code) {
|
||||
return (DatagramDnsResponse) super.setCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setId(int id) {
|
||||
return (DatagramDnsResponse) super.setId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setOpCode(DnsOpCode opCode) {
|
||||
return (DatagramDnsResponse) super.setOpCode(opCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setRecursionDesired(boolean recursionDesired) {
|
||||
return (DatagramDnsResponse) super.setRecursionDesired(recursionDesired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setZ(int z) {
|
||||
return (DatagramDnsResponse) super.setZ(z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse setRecord(DnsSection section, DnsRecord record) {
|
||||
return (DatagramDnsResponse) super.setRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse addRecord(DnsSection section, DnsRecord record) {
|
||||
return (DatagramDnsResponse) super.addRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse addRecord(DnsSection section, int index, DnsRecord record) {
|
||||
return (DatagramDnsResponse) super.addRecord(section, index, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse clear(DnsSection section) {
|
||||
return (DatagramDnsResponse) super.clear(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse clear() {
|
||||
return (DatagramDnsResponse) super.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse touch() {
|
||||
return (DatagramDnsResponse) super.touch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse touch(Object hint) {
|
||||
return (DatagramDnsResponse) super.touch(hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse retain() {
|
||||
return (DatagramDnsResponse) super.retain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramDnsResponse retain(int increment) {
|
||||
return (DatagramDnsResponse) 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* Decodes a {@link DatagramPacket} into a {@link DatagramDnsResponse}.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class DatagramDnsResponseDecoder extends MessageToMessageDecoder<DatagramPacket> {
|
||||
|
||||
private final DnsRecordDecoder recordDecoder;
|
||||
|
||||
/**
|
||||
* Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}.
|
||||
*/
|
||||
public DatagramDnsResponseDecoder() {
|
||||
this(DnsRecordDecoder.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder with the specified {@code recordDecoder}.
|
||||
*/
|
||||
public DatagramDnsResponseDecoder(DnsRecordDecoder recordDecoder) {
|
||||
this.recordDecoder = checkNotNull(recordDecoder, "recordDecoder");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
|
||||
final InetSocketAddress sender = packet.sender();
|
||||
final ByteBuf buf = packet.content();
|
||||
|
||||
final DnsResponse response = newResponse(sender, buf);
|
||||
boolean success = false;
|
||||
try {
|
||||
final int questionCount = buf.readUnsignedShort();
|
||||
final int answerCount = buf.readUnsignedShort();
|
||||
final int authorityRecordCount = buf.readUnsignedShort();
|
||||
final int additionalRecordCount = buf.readUnsignedShort();
|
||||
|
||||
decodeQuestions(response, buf, questionCount);
|
||||
decodeRecords(response, DnsSection.ANSWER, buf, answerCount);
|
||||
decodeRecords(response, DnsSection.AUTHORITY, buf, authorityRecordCount);
|
||||
decodeRecords(response, DnsSection.ADDITIONAL, buf, additionalRecordCount);
|
||||
|
||||
out.add(response);
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
response.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DnsResponse newResponse(InetSocketAddress sender, ByteBuf buf) {
|
||||
final int id = buf.readUnsignedShort();
|
||||
|
||||
final int flags = buf.readUnsignedShort();
|
||||
if (flags >> 15 == 0) {
|
||||
throw new CorruptedFrameException("not a response");
|
||||
}
|
||||
|
||||
final DnsResponse response = new DatagramDnsResponse(
|
||||
sender, null,
|
||||
id, DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)), DnsResponseCode.valueOf((byte) (flags & 0xf)));
|
||||
|
||||
response.setRecursionDesired((flags >> 8 & 1) == 1);
|
||||
response.setAuthoritativeAnswer((flags >> 10 & 1) == 1);
|
||||
response.setTruncated((flags >> 9 & 1) == 1);
|
||||
response.setRecursionAvailable((flags >> 7 & 1) == 1);
|
||||
response.setZ(flags >> 4 & 0x7);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void decodeQuestions(DnsResponse response, ByteBuf buf, int questionCount) throws Exception {
|
||||
for (int i = questionCount; i > 0; i --) {
|
||||
response.addRecord(DnsSection.QUESTION, recordDecoder.decodeQuestion(buf));
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeRecords(
|
||||
DnsResponse response, DnsSection section, ByteBuf buf, int count) throws Exception {
|
||||
for (int i = count; i > 0; i --) {
|
||||
response.addRecord(section, recordDecoder.decodeRecord(buf));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The default {@link DnsQuery} implementation.
|
||||
*/
|
||||
public class DefaultDnsQuery extends AbstractDnsMessage implements DnsQuery {
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode}.
|
||||
*
|
||||
* @param id the {@code ID} of the DNS query
|
||||
*/
|
||||
public DefaultDnsQuery(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param id the {@code ID} of the DNS query
|
||||
* @param opCode the {@code opCode} of the DNS query
|
||||
*/
|
||||
public DefaultDnsQuery(int id, DnsOpCode opCode) {
|
||||
super(id, opCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery setId(int id) {
|
||||
return (DnsQuery) super.setId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery setOpCode(DnsOpCode opCode) {
|
||||
return (DnsQuery) super.setOpCode(opCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery setRecursionDesired(boolean recursionDesired) {
|
||||
return (DnsQuery) super.setRecursionDesired(recursionDesired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery setZ(int z) {
|
||||
return (DnsQuery) super.setZ(z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery setRecord(DnsSection section, DnsRecord record) {
|
||||
return (DnsQuery) super.setRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery addRecord(DnsSection section, DnsRecord record) {
|
||||
return (DnsQuery) super.addRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery addRecord(DnsSection section, int index, DnsRecord record) {
|
||||
return (DnsQuery) super.addRecord(section, index, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery clear(DnsSection section) {
|
||||
return (DnsQuery) super.clear(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery clear() {
|
||||
return (DnsQuery) super.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery touch() {
|
||||
return (DnsQuery) super.touch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery touch(Object hint) {
|
||||
return (DnsQuery) super.touch(hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery retain() {
|
||||
return (DnsQuery) super.retain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQuery retain(int increment) {
|
||||
return (DnsQuery) super.retain(increment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DnsMessageUtil.appendQuery(new StringBuilder(128), this).toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The default {@link DnsQuestion} implementation.
|
||||
*/
|
||||
public class DefaultDnsQuestion extends AbstractDnsRecord implements DnsQuestion {
|
||||
|
||||
/**
|
||||
* Creates a new {@link #CLASS_IN IN-class} question.
|
||||
*
|
||||
* @param name the domain name of the DNS question
|
||||
* @param type the type of the DNS question
|
||||
*/
|
||||
public DefaultDnsQuestion(String name, DnsRecordType type) {
|
||||
super(name, type, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new question.
|
||||
*
|
||||
* @param name the domain name of the DNS question
|
||||
* @param type the type of the DNS question
|
||||
* @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>
|
||||
*/
|
||||
public DefaultDnsQuestion(String name, DnsRecordType type, int dnsClass) {
|
||||
super(name, type, dnsClass, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
|
||||
buf.append(StringUtil.simpleClassName(this))
|
||||
.append('(')
|
||||
.append(name())
|
||||
.append(' ');
|
||||
|
||||
DnsMessageUtil.appendRecordClass(buf, dnsClass())
|
||||
.append(' ')
|
||||
.append(type().name())
|
||||
.append(')');
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* The default {@code DnsRawRecord} implementation.
|
||||
*/
|
||||
public class DefaultDnsRawRecord extends AbstractDnsRecord implements DnsRawRecord {
|
||||
|
||||
private final ByteBuf content;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public DefaultDnsRawRecord(String name, DnsRecordType type, long timeToLive, ByteBuf content) {
|
||||
this(name, type, DnsRecord.CLASS_IN, timeToLive, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public DefaultDnsRawRecord(
|
||||
String name, DnsRecordType type, int dnsClass, long timeToLive, ByteBuf content) {
|
||||
super(name, type, dnsClass, timeToLive);
|
||||
this.content = checkNotNull(content, "content");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord copy() {
|
||||
return new DefaultDnsRawRecord(name(), type(), dnsClass(), timeToLive(), content().copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord duplicate() {
|
||||
return new DefaultDnsRawRecord(name(), type(), dnsClass(), timeToLive(), content().duplicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int refCnt() {
|
||||
return content().refCnt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord retain() {
|
||||
content().retain();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord retain(int increment) {
|
||||
content().retain(increment);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
return content().release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(int decrement) {
|
||||
return content().release(decrement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord touch() {
|
||||
content().touch();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsRawRecord touch(Object hint) {
|
||||
content().touch(hint);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder buf = new StringBuilder(64).append(StringUtil.simpleClassName(this)).append('(');
|
||||
final DnsRecordType type = type();
|
||||
if (type != DnsRecordType.OPT) {
|
||||
buf.append(name().isEmpty()? "<root>" : name())
|
||||
.append(' ')
|
||||
.append(timeToLive())
|
||||
.append(' ');
|
||||
|
||||
DnsMessageUtil.appendRecordClass(buf, dnsClass())
|
||||
.append(' ')
|
||||
.append(type.name());
|
||||
} else {
|
||||
buf.append("OPT flags:")
|
||||
.append(timeToLive())
|
||||
.append(" udp:")
|
||||
.append(dnsClass());
|
||||
}
|
||||
|
||||
buf.append(' ')
|
||||
.append(content().readableBytes())
|
||||
.append("B)");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link DnsRecordDecoder} implementation.
|
||||
*
|
||||
* @see DefaultDnsRecordEncoder
|
||||
*/
|
||||
public class DefaultDnsRecordDecoder implements DnsRecordDecoder {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
protected DefaultDnsRecordDecoder() { }
|
||||
|
||||
@Override
|
||||
public final DnsQuestion decodeQuestion(ByteBuf in) throws Exception {
|
||||
String name = decodeName(in);
|
||||
DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort());
|
||||
int qClass = in.readUnsignedShort();
|
||||
return new DefaultDnsQuestion(name, type, qClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <T extends DnsRecord> T decodeRecord(ByteBuf in) throws Exception {
|
||||
final String name = decodeName(in);
|
||||
final DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort());
|
||||
final int aClass = in.readUnsignedShort();
|
||||
final long ttl = in.readUnsignedInt();
|
||||
final int length = in.readUnsignedShort();
|
||||
final int offset = in.readerIndex();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T record = (T) decodeRecord(name, type, aClass, ttl, in, offset, length);
|
||||
in.readerIndex(offset + length);
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a record from the information decoded so far by {@link #decodeRecord(ByteBuf)}.
|
||||
*
|
||||
* @param name the domain name of the record
|
||||
* @param type the type of the record
|
||||
* @param dnsClass the class of the record
|
||||
* @param timeToLive the TTL of the record
|
||||
* @param in the {@link ByteBuf} that contains the RDATA
|
||||
* @param offset the start offset of the RDATA in {@code in}
|
||||
* @param length the length of the RDATA
|
||||
*
|
||||
* @return a {@link DnsRawRecord}. Override this method to decode RDATA and return other record implementation.
|
||||
*/
|
||||
protected DnsRecord decodeRecord(
|
||||
String name, DnsRecordType type, int dnsClass, long timeToLive,
|
||||
ByteBuf in, int offset, int length) throws Exception {
|
||||
|
||||
return new DefaultDnsRawRecord(
|
||||
name, type, dnsClass, timeToLive, in.duplicate().setIndex(offset, offset + length).retain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a domain name given a buffer containing a DNS packet. If the
|
||||
* name contains a pointer, the position of the buffer will be set to
|
||||
* directly after the pointer's index after the name has been read.
|
||||
*
|
||||
* @param in the byte buffer containing the DNS packet
|
||||
* @return the domain name for an entry
|
||||
*/
|
||||
protected String decodeName(ByteBuf in) {
|
||||
int position = -1;
|
||||
int checked = 0;
|
||||
final int end = in.writerIndex();
|
||||
final StringBuilder name = new StringBuilder(in.readableBytes() << 1);
|
||||
for (int len = in.readUnsignedByte(); in.isReadable() && len != 0; len = in.readUnsignedByte()) {
|
||||
boolean pointer = (len & 0xc0) == 0xc0;
|
||||
if (pointer) {
|
||||
if (position == -1) {
|
||||
position = in.readerIndex() + 1;
|
||||
}
|
||||
|
||||
final int next = (len & 0x3f) << 8 | in.readUnsignedByte();
|
||||
if (next >= end) {
|
||||
throw new CorruptedFrameException("name has an out-of-range pointer");
|
||||
}
|
||||
in.readerIndex(next);
|
||||
|
||||
// check for loops
|
||||
checked += 2;
|
||||
if (checked >= end) {
|
||||
throw new CorruptedFrameException("name contains a loop.");
|
||||
}
|
||||
} else {
|
||||
name.append(in.toString(in.readerIndex(), len, CharsetUtil.UTF_8)).append('.');
|
||||
in.skipBytes(len);
|
||||
}
|
||||
}
|
||||
if (position != -1) {
|
||||
in.readerIndex(position);
|
||||
}
|
||||
if (name.length() == 0) {
|
||||
return StringUtil.EMPTY_STRING;
|
||||
}
|
||||
|
||||
return name.substring(0, name.length() - 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The default {@link DnsRecordEncoder} implementation.
|
||||
*
|
||||
* @see DefaultDnsRecordDecoder
|
||||
*/
|
||||
public class DefaultDnsRecordEncoder implements DnsRecordEncoder {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
protected DefaultDnsRecordEncoder() { }
|
||||
|
||||
@Override
|
||||
public final void encodeQuestion(DnsQuestion question, ByteBuf out) throws Exception {
|
||||
encodeName(question.name(), out);
|
||||
out.writeShort(question.type().intValue());
|
||||
out.writeShort(question.dnsClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeRecord(DnsRecord record, ByteBuf out) throws Exception {
|
||||
if (record instanceof DnsQuestion) {
|
||||
encodeQuestion((DnsQuestion) record, out);
|
||||
} else if (record instanceof DnsRawRecord) {
|
||||
encodeRawRecord((DnsRawRecord) record, out);
|
||||
} else {
|
||||
throw new UnsupportedMessageTypeException(StringUtil.simpleClassName(record));
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeRawRecord(DnsRawRecord record, ByteBuf out) throws Exception {
|
||||
encodeName(record.name(), out);
|
||||
|
||||
out.writeShort(record.type().intValue());
|
||||
out.writeShort(record.dnsClass());
|
||||
out.writeInt((int) record.timeToLive());
|
||||
|
||||
ByteBuf content = record.content();
|
||||
int contentLen = content.readableBytes();
|
||||
|
||||
out.writeShort(contentLen);
|
||||
out.writeBytes(content, content.readerIndex(), contentLen);
|
||||
}
|
||||
|
||||
protected void encodeName(String name, ByteBuf buf) throws Exception {
|
||||
String[] parts = StringUtil.split(name, '.');
|
||||
for (String part: parts) {
|
||||
final int partLen = part.length();
|
||||
if (partLen == 0) {
|
||||
continue;
|
||||
}
|
||||
buf.writeByte(partLen);
|
||||
ByteBufUtil.writeAscii(buf, part);
|
||||
}
|
||||
buf.writeByte(0); // marks end of name field
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* The default {@link DnsResponse} implementation.
|
||||
*/
|
||||
public class DefaultDnsResponse extends AbstractDnsMessage implements DnsResponse {
|
||||
|
||||
private boolean authoritativeAnswer;
|
||||
private boolean truncated;
|
||||
private boolean recursionAvailable;
|
||||
private DnsResponseCode code;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode} and
|
||||
* the {@link DnsResponseCode#NOERROR} {@code RCODE}.
|
||||
*
|
||||
* @param id the {@code ID} of the DNS response
|
||||
*/
|
||||
public DefaultDnsResponse(int id) {
|
||||
this(id, DnsOpCode.QUERY, DnsResponseCode.NOERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link DnsResponseCode#NOERROR} {@code RCODE}.
|
||||
*
|
||||
* @param id the {@code ID} of the DNS response
|
||||
* @param opCode the {@code opCode} of the DNS response
|
||||
*/
|
||||
public DefaultDnsResponse(int id, DnsOpCode opCode) {
|
||||
this(id, opCode, DnsResponseCode.NOERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param id the {@code ID} of the DNS response
|
||||
* @param opCode the {@code opCode} of the DNS response
|
||||
* @param code the {@code RCODE} of the DNS response
|
||||
*/
|
||||
public DefaultDnsResponse(int id, DnsOpCode opCode, DnsResponseCode code) {
|
||||
super(id, opCode);
|
||||
setCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthoritativeAnswer() {
|
||||
return authoritativeAnswer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer) {
|
||||
this.authoritativeAnswer = authoritativeAnswer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTruncated() {
|
||||
return truncated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setTruncated(boolean truncated) {
|
||||
this.truncated = truncated;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecursionAvailable() {
|
||||
return recursionAvailable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setRecursionAvailable(boolean recursionAvailable) {
|
||||
this.recursionAvailable = recursionAvailable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponseCode code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setCode(DnsResponseCode code) {
|
||||
this.code = checkNotNull(code, "code");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setId(int id) {
|
||||
return (DnsResponse) super.setId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setOpCode(DnsOpCode opCode) {
|
||||
return (DnsResponse) super.setOpCode(opCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setRecursionDesired(boolean recursionDesired) {
|
||||
return (DnsResponse) super.setRecursionDesired(recursionDesired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setZ(int z) {
|
||||
return (DnsResponse) super.setZ(z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse setRecord(DnsSection section, DnsRecord record) {
|
||||
return (DnsResponse) super.setRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse addRecord(DnsSection section, DnsRecord record) {
|
||||
return (DnsResponse) super.addRecord(section, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse addRecord(DnsSection section, int index, DnsRecord record) {
|
||||
return (DnsResponse) super.addRecord(section, index, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse clear(DnsSection section) {
|
||||
return (DnsResponse) super.clear(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse clear() {
|
||||
return (DnsResponse) super.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponse touch() {
|
||||
return (DnsResponse) super.touch();
|
||||
}
|
||||
|
||||
@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 String toString() {
|
||||
return DnsMessageUtil.appendResponse(new StringBuilder(128), this).toString();
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 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;
|
||||
|
||||
/**
|
||||
* Represents a class field in DNS protocol
|
||||
*/
|
||||
public final class DnsClass implements Comparable<DnsClass> {
|
||||
|
||||
/**
|
||||
* Default class for DNS entries.
|
||||
*/
|
||||
public static final DnsClass IN = new DnsClass(0x0001, "IN");
|
||||
public static final DnsClass CSNET = new DnsClass(0x0002, "CSNET");
|
||||
public static final DnsClass CHAOS = new DnsClass(0x0003, "CHAOS");
|
||||
public static final DnsClass HESIOD = new DnsClass(0x0004, "HESIOD");
|
||||
public static final DnsClass NONE = new DnsClass(0x00fe, "NONE");
|
||||
public static final DnsClass ANY = new DnsClass(0x00ff, "ANY");
|
||||
|
||||
private static final String EXPECTED =
|
||||
" (expected: " +
|
||||
IN + '(' + IN.intValue() + "), " +
|
||||
CSNET + '(' + CSNET.intValue() + "), " +
|
||||
CHAOS + '(' + CHAOS.intValue() + "), " +
|
||||
HESIOD + '(' + HESIOD.intValue() + "), " +
|
||||
NONE + '(' + NONE.intValue() + "), " +
|
||||
ANY + '(' + ANY.intValue() + "))";
|
||||
|
||||
public static DnsClass valueOf(String name) {
|
||||
if (IN.name().equals(name)) {
|
||||
return IN;
|
||||
}
|
||||
if (NONE.name().equals(name)) {
|
||||
return NONE;
|
||||
}
|
||||
if (ANY.name().equals(name)) {
|
||||
return ANY;
|
||||
}
|
||||
if (CSNET.name().equals(name)) {
|
||||
return CSNET;
|
||||
}
|
||||
if (CHAOS.name().equals(name)) {
|
||||
return CHAOS;
|
||||
}
|
||||
if (HESIOD.name().equals(name)) {
|
||||
return HESIOD;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("name: " + name + EXPECTED);
|
||||
}
|
||||
|
||||
public static DnsClass valueOf(int intValue) {
|
||||
switch (intValue) {
|
||||
case 0x0001:
|
||||
return IN;
|
||||
case 0x0002:
|
||||
return CSNET;
|
||||
case 0x0003:
|
||||
return CHAOS;
|
||||
case 0x0004:
|
||||
return HESIOD;
|
||||
case 0x00fe:
|
||||
return NONE;
|
||||
case 0x00ff:
|
||||
return ANY;
|
||||
default:
|
||||
return new DnsClass(intValue, "UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of DnsClass for a custom type.
|
||||
*
|
||||
* @param clazz The class
|
||||
* @param name The name
|
||||
*/
|
||||
public static DnsClass valueOf(int clazz, String name) {
|
||||
return new DnsClass(clazz, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The protocol value of this DNS class
|
||||
*/
|
||||
private final int intValue;
|
||||
|
||||
/**
|
||||
* The name of this DNS class
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
private DnsClass(int intValue, String name) {
|
||||
if ((intValue & 0xffff) != intValue) {
|
||||
throw new IllegalArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)");
|
||||
}
|
||||
|
||||
this.intValue = intValue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this class as used in bind config files
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the protocol value represented by this class
|
||||
*/
|
||||
public int intValue() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof DnsClass && ((DnsClass) o).intValue == intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DnsClass o) {
|
||||
return intValue() - o.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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;
|
||||
|
||||
/**
|
||||
* A class representing entries in a DNS packet (questions, and all resource
|
||||
* records). Contains data shared by entries such as name, type, and class.
|
||||
*/
|
||||
public class DnsEntry {
|
||||
|
||||
private final String name;
|
||||
private final DnsType type;
|
||||
private final DnsClass dnsClass;
|
||||
|
||||
// only allow to extend from same package
|
||||
DnsEntry(String name, DnsType type, DnsClass dnsClass) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name");
|
||||
}
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type");
|
||||
}
|
||||
if (dnsClass == null) {
|
||||
throw new NullPointerException("dnsClass");
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.dnsClass = dnsClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this entry (the domain).
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of resource record to be received.
|
||||
*/
|
||||
public DnsType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class for this entry. Default is IN (Internet).
|
||||
*/
|
||||
public DnsClass dnsClass() {
|
||||
return dnsClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (name.hashCode() * 31 + type.hashCode()) * 31 + dnsClass.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(128).append(StringUtil.simpleClassName(this))
|
||||
.append("(name: ").append(name)
|
||||
.append(", type: ").append(type)
|
||||
.append(", class: ").append(dnsClass)
|
||||
.append(')').toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof DnsEntry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DnsEntry that = (DnsEntry) o;
|
||||
return type().intValue() == that.type().intValue() &&
|
||||
dnsClass().intValue() == that.dnsClass().intValue() &&
|
||||
name().equals(that.name());
|
||||
}
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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;
|
||||
|
||||
/**
|
||||
* The header super-class which includes information shared by DNS query and
|
||||
* response packet headers such as the ID, opcode, and type. The only flag
|
||||
* shared by both classes is the flag for desiring recursion.
|
||||
*/
|
||||
public class DnsHeader {
|
||||
|
||||
/**
|
||||
* Message type is query.
|
||||
*/
|
||||
public static final int TYPE_QUERY = 0;
|
||||
|
||||
/**
|
||||
* Message type is response.
|
||||
*/
|
||||
public static final int TYPE_RESPONSE = 1;
|
||||
|
||||
/**
|
||||
* Message is for a standard query.
|
||||
*/
|
||||
public static final int OPCODE_QUERY = 0;
|
||||
|
||||
/**
|
||||
* Message is for an inverse query. <strong>Note: inverse queries have been
|
||||
* obsoleted since RFC 3425, and are not necessarily supported.</strong>
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int OPCODE_IQUERY = 1;
|
||||
|
||||
private final DnsMessage parent;
|
||||
|
||||
private boolean recursionDesired;
|
||||
private int opcode;
|
||||
private int id;
|
||||
private int type;
|
||||
private int z;
|
||||
|
||||
// only allow to extend from within the same package
|
||||
DnsHeader(DnsMessage parent) {
|
||||
if (parent == null) {
|
||||
throw new NullPointerException("parent");
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of questions in the {@link DnsMessage}.
|
||||
*/
|
||||
public int questionCount() {
|
||||
return parent.questions().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of answer resource records in the {@link DnsMessage}.
|
||||
*/
|
||||
public int answerCount() {
|
||||
return parent.answers().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of authority resource records in the
|
||||
* {@link DnsMessage}.
|
||||
*/
|
||||
public int authorityResourceCount() {
|
||||
return parent.authorityResources().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of additional resource records in the
|
||||
* {@link DnsMessage}.
|
||||
*/
|
||||
public int additionalResourceCount() {
|
||||
return parent.additionalResources().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a query is to be pursued recursively.
|
||||
*/
|
||||
public boolean isRecursionDesired() {
|
||||
return recursionDesired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 4 bit opcode used for the {@link DnsMessage}.
|
||||
*
|
||||
* @see #OPCODE_QUERY
|
||||
* @see #OPCODE_IQUERY
|
||||
*/
|
||||
public int opcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of {@link DnsMessage}.
|
||||
*
|
||||
* @see #TYPE_QUERY
|
||||
*/
|
||||
public int type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 2 byte unsigned identifier number used for the
|
||||
* {@link DnsMessage}.
|
||||
*/
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the opcode for this {@link DnsMessage}.
|
||||
*
|
||||
* @param opcode
|
||||
* opcode to set
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
public DnsHeader setOpcode(int opcode) {
|
||||
this.opcode = opcode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether a name server is directed to pursue a query recursively or
|
||||
* not.
|
||||
*
|
||||
* @param recursionDesired
|
||||
* if set to {@code true}, pursues query recursively
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
public DnsHeader setRecursionDesired(boolean recursionDesired) {
|
||||
this.recursionDesired = recursionDesired;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link DnsMessage} type.
|
||||
*
|
||||
* @param type
|
||||
* message type
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
public DnsHeader setType(int type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id for this {@link DnsMessage}.
|
||||
*
|
||||
* @param id
|
||||
* a unique 2 byte unsigned identifier
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
public DnsHeader setId(int id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 3 bit reserved field 'Z'.
|
||||
*/
|
||||
public int z() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field Z. This field is reserved and should remain as 0 if the
|
||||
* DNS server does not make usage of this field.
|
||||
*
|
||||
* @param z
|
||||
* the value for the reserved field Z
|
||||
*/
|
||||
public DnsHeader setZ(int z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* 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
|
||||
@ -15,225 +15,142 @@
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import io.netty.util.AbstractReferenceCounted;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
|
||||
/**
|
||||
* The message super-class which contains core information concerning DNS
|
||||
* packets, both outgoing and incoming.
|
||||
* The superclass which contains core information concerning a {@link DnsQuery} and a {@link DnsResponse}.
|
||||
*/
|
||||
public abstract class DnsMessage extends AbstractReferenceCounted {
|
||||
|
||||
private List<DnsQuestion> questions;
|
||||
private List<DnsResource> answers;
|
||||
private List<DnsResource> authority;
|
||||
private List<DnsResource> additional;
|
||||
|
||||
private final DnsHeader header;
|
||||
|
||||
// Only allow to extend from same package
|
||||
DnsMessage(int id) {
|
||||
header = newHeader(id);
|
||||
}
|
||||
public interface DnsMessage extends ReferenceCounted {
|
||||
|
||||
/**
|
||||
* Returns the header belonging to this message.
|
||||
* Returns the {@code ID} of this DNS message.
|
||||
*/
|
||||
public DnsHeader header() {
|
||||
return header;
|
||||
}
|
||||
int id();
|
||||
|
||||
/**
|
||||
* Returns a list of all the questions in this message.
|
||||
* Sets the {@code ID} of this DNS message.
|
||||
*/
|
||||
public List<DnsQuestion> questions() {
|
||||
if (questions == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(questions);
|
||||
}
|
||||
DnsMessage setId(int id);
|
||||
|
||||
/**
|
||||
* Returns a list of all the answer resource records in this message.
|
||||
* Returns the {@code opCode} of this DNS message.
|
||||
*/
|
||||
public List<DnsResource> answers() {
|
||||
if (answers == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(answers);
|
||||
}
|
||||
DnsOpCode opCode();
|
||||
|
||||
/**
|
||||
* Returns a list of all the authority resource records in this message.
|
||||
* Sets the {@code opCode} of this DNS message.
|
||||
*/
|
||||
public List<DnsResource> authorityResources() {
|
||||
if (authority == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(authority);
|
||||
}
|
||||
DnsMessage setOpCode(DnsOpCode opCode);
|
||||
|
||||
/**
|
||||
* Returns a list of all the additional resource records in this message.
|
||||
* Returns the {@code RD} (recursion desired} field of this DNS message.
|
||||
*/
|
||||
public List<DnsResource> additionalResources() {
|
||||
if (additional == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(additional);
|
||||
}
|
||||
boolean isRecursionDesired();
|
||||
|
||||
/**
|
||||
* Adds an answer resource record to this message.
|
||||
* Sets the {@code RD} (recursion desired} field of this DNS message.
|
||||
*/
|
||||
DnsMessage setRecursionDesired(boolean recursionDesired);
|
||||
|
||||
/**
|
||||
* Returns the {@code Z} (reserved for future use) field of this DNS message.
|
||||
*/
|
||||
int z();
|
||||
|
||||
/**
|
||||
* Sets the {@code Z} (reserved for future use) field of this DNS message.
|
||||
*/
|
||||
DnsMessage setZ(int z);
|
||||
|
||||
/**
|
||||
* Returns the number of records in the specified {@code section} of this DNS message.
|
||||
*/
|
||||
int count(DnsSection section);
|
||||
|
||||
/**
|
||||
* Returns the number of records in this DNS message.
|
||||
*/
|
||||
int count();
|
||||
|
||||
/**
|
||||
* Returns the first record in the specified {@code section} of this DNS message.
|
||||
* When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is
|
||||
* always {@link DnsQuestion}.
|
||||
*
|
||||
* @param answer
|
||||
* the answer resource record to be added
|
||||
* @return the message to allow method chaining
|
||||
* @return {@code null} if this message doesn't have any records in the specified {@code section}
|
||||
*/
|
||||
public DnsMessage addAnswer(DnsResource answer) {
|
||||
if (answers == null) {
|
||||
answers = new LinkedList<DnsResource>();
|
||||
}
|
||||
answers.add(answer);
|
||||
return this;
|
||||
}
|
||||
<T extends DnsRecord> T recordAt(DnsSection section);
|
||||
|
||||
/**
|
||||
* Adds a question to this message.
|
||||
* Returns the record at the specified {@code index} of the specified {@code section} of this DNS message.
|
||||
* When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is
|
||||
* always {@link DnsQuestion}.
|
||||
*
|
||||
* @param question
|
||||
* the question to be added
|
||||
* @return the message to allow method chaining
|
||||
* @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds
|
||||
*/
|
||||
public DnsMessage addQuestion(DnsQuestion question) {
|
||||
if (questions == null) {
|
||||
questions = new LinkedList<DnsQuestion>();
|
||||
}
|
||||
questions.add(question);
|
||||
return this;
|
||||
}
|
||||
<T extends DnsRecord> T recordAt(DnsSection section, int index);
|
||||
|
||||
/**
|
||||
* Adds an authority resource record to this message.
|
||||
*
|
||||
* @param resource
|
||||
* the authority resource record to be added
|
||||
* @return the message to allow method chaining
|
||||
* Sets the specified {@code section} of this DNS message to the specified {@code record},
|
||||
* making it a single-record section. When the specified {@code section} is {@link DnsSection#QUESTION},
|
||||
* the specified {@code record} must be a {@link DnsQuestion}.
|
||||
*/
|
||||
public DnsMessage addAuthorityResource(DnsResource resource) {
|
||||
if (authority == null) {
|
||||
authority = new LinkedList<DnsResource>();
|
||||
}
|
||||
authority.add(resource);
|
||||
return this;
|
||||
}
|
||||
DnsMessage setRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
/**
|
||||
* Adds an additional resource record to this message.
|
||||
* Sets the specified {@code record} at the specified {@code index} of the specified {@code section}
|
||||
* of this DNS message. When the specified {@code section} is {@link DnsSection#QUESTION},
|
||||
* the specified {@code record} must be a {@link DnsQuestion}.
|
||||
*
|
||||
* @param resource
|
||||
* the additional resource record to be added
|
||||
* @return the message to allow method chaining
|
||||
* @return the old record
|
||||
* @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds
|
||||
*/
|
||||
public DnsMessage addAdditionalResource(DnsResource resource) {
|
||||
if (additional == null) {
|
||||
additional = new LinkedList<DnsResource>();
|
||||
}
|
||||
additional.add(resource);
|
||||
return this;
|
||||
}
|
||||
<T extends DnsRecord> T setRecord(DnsSection section, int index, DnsRecord record);
|
||||
|
||||
/**
|
||||
* Adds the specified {@code record} at the end of the specified {@code section} of this DNS message.
|
||||
* When the specified {@code section} is {@link DnsSection#QUESTION}, the specified {@code record}
|
||||
* must be a {@link DnsQuestion}.
|
||||
*/
|
||||
DnsMessage addRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
/**
|
||||
* Adds the specified {@code record} at the specified {@code index} of the specified {@code section}
|
||||
* of this DNS message. When the specified {@code section} is {@link DnsSection#QUESTION}, the specified
|
||||
* {@code record} must be a {@link DnsQuestion}.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds
|
||||
*/
|
||||
DnsMessage addRecord(DnsSection section, int index, DnsRecord record);
|
||||
|
||||
/**
|
||||
* Removes the record at the specified {@code index} of the specified {@code section} from this DNS message.
|
||||
* When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is
|
||||
* always {@link DnsQuestion}.
|
||||
*
|
||||
* @return the removed record
|
||||
*/
|
||||
<T extends DnsRecord> T removeRecord(DnsSection section, int index);
|
||||
|
||||
/**
|
||||
* Removes all the records in the specified {@code section} of this DNS message.
|
||||
*/
|
||||
DnsMessage clear(DnsSection section);
|
||||
|
||||
/**
|
||||
* Removes all the records in this DNS message.
|
||||
*/
|
||||
DnsMessage clear();
|
||||
|
||||
@Override
|
||||
protected void deallocate() {
|
||||
// NOOP
|
||||
}
|
||||
DnsMessage touch();
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
release(questions());
|
||||
release(answers());
|
||||
release(additionalResources());
|
||||
release(authorityResources());
|
||||
return super.release();
|
||||
}
|
||||
|
||||
private static void release(List<?> resources) {
|
||||
for (Object resource: resources) {
|
||||
ReferenceCountUtil.release(resource);
|
||||
}
|
||||
}
|
||||
DnsMessage touch(Object hint);
|
||||
|
||||
@Override
|
||||
public boolean release(int decrement) {
|
||||
release(questions(), decrement);
|
||||
release(answers(), decrement);
|
||||
release(additionalResources(), decrement);
|
||||
release(authorityResources(), decrement);
|
||||
return super.release(decrement);
|
||||
}
|
||||
|
||||
private static void release(List<?> resources, int decrement) {
|
||||
for (Object resource: resources) {
|
||||
ReferenceCountUtil.release(resource, decrement);
|
||||
}
|
||||
}
|
||||
DnsMessage retain();
|
||||
|
||||
@Override
|
||||
public DnsMessage touch(Object hint) {
|
||||
touch(questions(), hint);
|
||||
touch(answers(), hint);
|
||||
touch(additionalResources(), hint);
|
||||
touch(authorityResources(), hint);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void touch(List<?> resources, Object hint) {
|
||||
for (Object resource: resources) {
|
||||
ReferenceCountUtil.touch(resource, hint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage retain() {
|
||||
retain(questions());
|
||||
retain(answers());
|
||||
retain(additionalResources());
|
||||
retain(authorityResources());
|
||||
super.retain();
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void retain(List<?> resources) {
|
||||
for (Object resource: resources) {
|
||||
ReferenceCountUtil.retain(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage retain(int increment) {
|
||||
retain(questions(), increment);
|
||||
retain(answers(), increment);
|
||||
retain(additionalResources(), increment);
|
||||
retain(authorityResources(), increment);
|
||||
super.retain(increment);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void retain(List<?> resources, int increment) {
|
||||
for (Object resource: resources) {
|
||||
ReferenceCountUtil.retain(resource, increment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsMessage touch() {
|
||||
super.touch();
|
||||
return this;
|
||||
}
|
||||
|
||||
protected abstract DnsHeader newHeader(int id);
|
||||
DnsMessage retain(int increment);
|
||||
}
|
||||
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* Provides some utility methods for DNS message implementations.
|
||||
*/
|
||||
final class DnsMessageUtil {
|
||||
|
||||
static StringBuilder appendQuery(StringBuilder buf, DnsQuery query) {
|
||||
appendQueryHeader(buf, query);
|
||||
appendAllRecords(buf, query);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static StringBuilder appendResponse(StringBuilder buf, DnsResponse response) {
|
||||
appendResponseHeader(buf, response);
|
||||
appendAllRecords(buf, response);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static StringBuilder appendRecordClass(StringBuilder buf, int dnsClass) {
|
||||
final String name;
|
||||
switch (dnsClass &= 0xFFFF) {
|
||||
case DnsRecord.CLASS_IN:
|
||||
name = "IN";
|
||||
break;
|
||||
case DnsRecord.CLASS_CSNET:
|
||||
name = "CSNET";
|
||||
break;
|
||||
case DnsRecord.CLASS_CHAOS:
|
||||
name = "CHAOS";
|
||||
break;
|
||||
case DnsRecord.CLASS_HESIOD:
|
||||
name = "HESIOD";
|
||||
break;
|
||||
case DnsRecord.CLASS_NONE:
|
||||
name = "NONE";
|
||||
break;
|
||||
case DnsRecord.CLASS_ANY:
|
||||
name = "ANY";
|
||||
break;
|
||||
default:
|
||||
name = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
buf.append(name);
|
||||
} else {
|
||||
buf.append("UNKNOWN(").append(dnsClass).append(')');
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static void appendQueryHeader(StringBuilder buf, DnsQuery msg) {
|
||||
buf.append(StringUtil.simpleClassName(msg))
|
||||
.append('(');
|
||||
|
||||
appendAddresses(buf, msg)
|
||||
.append(msg.id())
|
||||
.append(", ")
|
||||
.append(msg.opCode());
|
||||
|
||||
if (msg.isRecursionDesired()) {
|
||||
buf.append(", RD");
|
||||
}
|
||||
if (msg.z() != 0) {
|
||||
buf.append(", Z: ")
|
||||
.append(msg.z());
|
||||
}
|
||||
buf.append(')');
|
||||
}
|
||||
|
||||
private static void appendResponseHeader(StringBuilder buf, DnsResponse msg) {
|
||||
buf.append(StringUtil.simpleClassName(msg))
|
||||
.append('(');
|
||||
|
||||
appendAddresses(buf, msg)
|
||||
.append(msg.id())
|
||||
.append(", ")
|
||||
.append(msg.opCode())
|
||||
.append(", ")
|
||||
.append(msg.code())
|
||||
.append(',');
|
||||
|
||||
boolean hasComma = true;
|
||||
if (msg.isRecursionDesired()) {
|
||||
hasComma = false;
|
||||
buf.append(" RD");
|
||||
}
|
||||
if (msg.isAuthoritativeAnswer()) {
|
||||
hasComma = false;
|
||||
buf.append(" AA");
|
||||
}
|
||||
if (msg.isTruncated()) {
|
||||
hasComma = false;
|
||||
buf.append(" TC");
|
||||
}
|
||||
if (msg.isRecursionAvailable()) {
|
||||
hasComma = false;
|
||||
buf.append(" RA");
|
||||
}
|
||||
if (msg.z() != 0) {
|
||||
if (!hasComma) {
|
||||
buf.append(',');
|
||||
}
|
||||
buf.append(" Z: ")
|
||||
.append(msg.z());
|
||||
}
|
||||
|
||||
if (hasComma) {
|
||||
buf.setCharAt(buf.length() - 1, ')');
|
||||
} else {
|
||||
buf.append(')');
|
||||
}
|
||||
}
|
||||
|
||||
private static StringBuilder appendAddresses(StringBuilder buf, DnsMessage msg) {
|
||||
|
||||
if (!(msg instanceof AddressedEnvelope)) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
AddressedEnvelope<?, SocketAddress> envelope = (AddressedEnvelope<?, SocketAddress>) msg;
|
||||
|
||||
SocketAddress addr = envelope.sender();
|
||||
if (addr != null) {
|
||||
buf.append("from: ")
|
||||
.append(addr)
|
||||
.append(", ");
|
||||
}
|
||||
|
||||
addr = envelope.recipient();
|
||||
if (addr != null) {
|
||||
buf.append("to: ")
|
||||
.append(addr)
|
||||
.append(", ");
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static void appendAllRecords(StringBuilder buf, DnsMessage msg) {
|
||||
appendRecords(buf, msg, DnsSection.QUESTION);
|
||||
appendRecords(buf, msg, DnsSection.ANSWER);
|
||||
appendRecords(buf, msg, DnsSection.AUTHORITY);
|
||||
appendRecords(buf, msg, DnsSection.ADDITIONAL);
|
||||
}
|
||||
|
||||
private static void appendRecords(StringBuilder buf, DnsMessage message, DnsSection section) {
|
||||
final int count = message.count(section);
|
||||
for (int i = 0; i < count; i ++) {
|
||||
buf.append(StringUtil.NEWLINE)
|
||||
.append(StringUtil.TAB)
|
||||
.append(message.recordAt(section, i));
|
||||
}
|
||||
}
|
||||
|
||||
private DnsMessageUtil() { }
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* The DNS {@code OpCode} as defined in <a href="https://tools.ietf.org/html/rfc2929">RFC2929</a>.
|
||||
*/
|
||||
public class DnsOpCode implements Comparable<DnsOpCode> {
|
||||
|
||||
/**
|
||||
* The 'Query' DNS OpCode, as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsOpCode QUERY = new DnsOpCode(0x00, "QUERY");
|
||||
|
||||
/**
|
||||
* The 'IQuery' DNS OpCode, as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsOpCode IQUERY = new DnsOpCode(0x01, "IQUERY");
|
||||
|
||||
/**
|
||||
* The 'Status' DNS OpCode, as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsOpCode STATUS = new DnsOpCode(0x02, "STATUS");
|
||||
|
||||
/**
|
||||
* The 'Notify' DNS OpCode, as defined in <a href="https://tools.ietf.org/html/rfc1996">RFC1996</a>.
|
||||
*/
|
||||
public static final DnsOpCode NOTIFY = new DnsOpCode(0x04, "NOTIFY");
|
||||
|
||||
/**
|
||||
* The 'Update' DNS OpCode, as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsOpCode UPDATE = new DnsOpCode(0x05, "UPDATE");
|
||||
|
||||
/**
|
||||
* Returns the {@link DnsOpCode} instance of the specified byte value.
|
||||
*/
|
||||
public static DnsOpCode valueOf(int b) {
|
||||
switch (b) {
|
||||
case 0x00:
|
||||
return QUERY;
|
||||
case 0x01:
|
||||
return IQUERY;
|
||||
case 0x02:
|
||||
return STATUS;
|
||||
case 0x04:
|
||||
return NOTIFY;
|
||||
case 0x05:
|
||||
return UPDATE;
|
||||
}
|
||||
|
||||
return new DnsOpCode(b);
|
||||
}
|
||||
|
||||
private final byte byteValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
private DnsOpCode(int byteValue) {
|
||||
this(byteValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public DnsOpCode(int byteValue, String name) {
|
||||
this.byteValue = (byte) byteValue;
|
||||
this.name = checkNotNull(name, "name");
|
||||
}
|
||||
|
||||
public byte byteValue() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof DnsOpCode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byteValue == ((DnsOpCode) obj).byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DnsOpCode o) {
|
||||
return byteValue - o.byteValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + (byteValue & 0xFF) + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* 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
|
||||
@ -15,89 +15,46 @@
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* A DNS query packet which is sent to a server to receive a DNS response packet
|
||||
* with information answering a DnsQuery's questions.
|
||||
* A DNS query message.
|
||||
*/
|
||||
public class DnsQuery extends DnsMessage {
|
||||
|
||||
private final InetSocketAddress recipient;
|
||||
|
||||
/**
|
||||
* Constructs a DNS query. By default recursion will be toggled on.
|
||||
*/
|
||||
public DnsQuery(int id, InetSocketAddress recipient) {
|
||||
super(id);
|
||||
if (recipient == null) {
|
||||
throw new NullPointerException("recipient");
|
||||
}
|
||||
this.recipient = recipient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link InetSocketAddress} of the recipient of the {@link DnsQuery}
|
||||
*/
|
||||
public InetSocketAddress recipient() {
|
||||
return recipient;
|
||||
}
|
||||
public interface DnsQuery extends DnsMessage {
|
||||
@Override
|
||||
DnsQuery setId(int id);
|
||||
|
||||
@Override
|
||||
public DnsQuery addAnswer(DnsResource answer) {
|
||||
super.addAnswer(answer);
|
||||
return this;
|
||||
}
|
||||
DnsQuery setOpCode(DnsOpCode opCode);
|
||||
|
||||
@Override
|
||||
public DnsQuery addQuestion(DnsQuestion question) {
|
||||
super.addQuestion(question);
|
||||
return this;
|
||||
}
|
||||
DnsQuery setRecursionDesired(boolean recursionDesired);
|
||||
|
||||
@Override
|
||||
public DnsQuery addAuthorityResource(DnsResource resource) {
|
||||
super.addAuthorityResource(resource);
|
||||
return this;
|
||||
}
|
||||
DnsQuery setZ(int z);
|
||||
|
||||
@Override
|
||||
public DnsQuery addAdditionalResource(DnsResource resource) {
|
||||
super.addAdditionalResource(resource);
|
||||
return this;
|
||||
}
|
||||
DnsQuery setRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsQuery touch(Object hint) {
|
||||
super.touch(hint);
|
||||
return this;
|
||||
}
|
||||
DnsQuery addRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsQuery retain() {
|
||||
super.retain();
|
||||
return this;
|
||||
}
|
||||
DnsQuery addRecord(DnsSection section, int index, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsQuery retain(int increment) {
|
||||
super.retain(increment);
|
||||
return this;
|
||||
}
|
||||
DnsQuery clear(DnsSection section);
|
||||
|
||||
@Override
|
||||
public DnsQuery touch() {
|
||||
super.touch();
|
||||
return this;
|
||||
}
|
||||
DnsQuery clear();
|
||||
|
||||
@Override
|
||||
public DnsQueryHeader header() {
|
||||
return (DnsQueryHeader) super.header();
|
||||
}
|
||||
DnsQuery touch();
|
||||
|
||||
@Override
|
||||
protected DnsQueryHeader newHeader(int id) {
|
||||
return new DnsQueryHeader(this, id);
|
||||
}
|
||||
DnsQuery touch(Object hint);
|
||||
|
||||
@Override
|
||||
DnsQuery retain();
|
||||
|
||||
@Override
|
||||
DnsQuery retain(int increment);
|
||||
}
|
||||
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DnsQueryEncoder accepts {@link DnsQuery} and encodes to {@link ByteBuf}. This
|
||||
* class also contains methods for encoding parts of DnsQuery's such as the
|
||||
* header and questions.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class DnsQueryEncoder extends MessageToMessageEncoder<DnsQuery> {
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, DnsQuery query, List<Object> out) throws Exception {
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
encodeHeader(query.header(), buf);
|
||||
List<DnsQuestion> questions = query.questions();
|
||||
for (DnsQuestion question : questions) {
|
||||
encodeQuestion(question, CharsetUtil.US_ASCII, buf);
|
||||
}
|
||||
for (DnsResource resource: query.additionalResources()) {
|
||||
encodeResource(resource, CharsetUtil.US_ASCII, buf);
|
||||
}
|
||||
out.add(new DatagramPacket(buf, query.recipient(), null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the information in a {@link DnsHeader} and writes it to the
|
||||
* specified {@link ByteBuf}. The header is always 12 bytes long.
|
||||
*
|
||||
* @param header
|
||||
* the query header being encoded
|
||||
* @param buf
|
||||
* the buffer the encoded data should be written to
|
||||
*/
|
||||
private static void encodeHeader(DnsHeader header, ByteBuf buf) {
|
||||
buf.writeShort(header.id());
|
||||
int flags = 0;
|
||||
flags |= header.type() << 15;
|
||||
flags |= header.opcode() << 14;
|
||||
flags |= header.isRecursionDesired() ? 1 << 8 : 0;
|
||||
buf.writeShort(flags);
|
||||
buf.writeShort(header.questionCount());
|
||||
buf.writeShort(0); // answerCount
|
||||
buf.writeShort(0); // authorityResourceCount
|
||||
buf.writeShort(header.additionalResourceCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the information in a {@link DnsQuestion} and writes it to the
|
||||
* specified {@link ByteBuf}.
|
||||
*
|
||||
* @param question
|
||||
* the question being encoded
|
||||
* @param charset
|
||||
* charset names are encoded in
|
||||
* @param buf
|
||||
* the buffer the encoded data should be written to
|
||||
*/
|
||||
private static void encodeQuestion(DnsQuestion question, Charset charset, ByteBuf buf) {
|
||||
encodeName(question.name(), charset, buf);
|
||||
buf.writeShort(question.type().intValue());
|
||||
buf.writeShort(question.dnsClass().intValue());
|
||||
}
|
||||
|
||||
private static void encodeResource(DnsResource resource, Charset charset, ByteBuf buf) {
|
||||
encodeName(resource.name(), charset, buf);
|
||||
|
||||
buf.writeShort(resource.type().intValue());
|
||||
buf.writeShort(resource.dnsClass().intValue());
|
||||
buf.writeInt((int) resource.timeToLive());
|
||||
|
||||
ByteBuf content = resource.content();
|
||||
int contentLen = content.readableBytes();
|
||||
|
||||
buf.writeShort(contentLen);
|
||||
buf.writeBytes(content, content.readerIndex(), contentLen);
|
||||
}
|
||||
|
||||
private static void encodeName(String name, Charset charset, ByteBuf buf) {
|
||||
String[] parts = StringUtil.split(name, '.');
|
||||
for (String part: parts) {
|
||||
final int partLen = part.length();
|
||||
if (partLen == 0) {
|
||||
continue;
|
||||
}
|
||||
buf.writeByte(partLen);
|
||||
buf.writeBytes(part.getBytes(charset));
|
||||
}
|
||||
buf.writeByte(0); // marks end of name field
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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;
|
||||
|
||||
/**
|
||||
* The DNS query header class which is used to represent the 12 byte header in a
|
||||
* {@link DnsQuery}.
|
||||
*/
|
||||
public final class DnsQueryHeader extends DnsHeader {
|
||||
|
||||
/**
|
||||
* Constructor for a DNS packet query header. The id is user generated and
|
||||
* will be replicated in the response packet by the server.
|
||||
*
|
||||
* @param parent the {@link DnsMessage} this header belongs to
|
||||
* @param id a 2 bit unsigned identification number for this query
|
||||
*/
|
||||
public DnsQueryHeader(DnsMessage parent, int id) {
|
||||
super(parent);
|
||||
setId(id);
|
||||
setRecursionDesired(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DnsMessage} type. This will always return
|
||||
* {@code TYPE_QUERY}.
|
||||
*/
|
||||
@Override
|
||||
public int type() {
|
||||
return TYPE_QUERY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link DnsHeader} type. Must be {@code TYPE_RESPONSE}.
|
||||
*
|
||||
* @param type message type
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
@Override
|
||||
public DnsQueryHeader setType(int type) {
|
||||
if (type != TYPE_QUERY) {
|
||||
throw new IllegalArgumentException("type cannot be anything but TYPE_QUERY (0) for a query header.");
|
||||
}
|
||||
super.setType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQueryHeader setId(int id) {
|
||||
super.setId(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQueryHeader setRecursionDesired(boolean recursionDesired) {
|
||||
super.setRecursionDesired(recursionDesired);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQueryHeader setOpcode(int opcode) {
|
||||
super.setOpcode(opcode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsQueryHeader setZ(int z) {
|
||||
super.setZ(z);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* 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
|
||||
@ -15,58 +15,13 @@
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import static io.netty.handler.codec.dns.DnsClass.IN;
|
||||
|
||||
/**
|
||||
* The DNS question class which represents a question being sent to a server via
|
||||
* a query, or the question being duplicated and sent back in a response.
|
||||
* Usually a message contains a single question, and DNS servers often don't
|
||||
* support multiple questions in a single query.
|
||||
* A DNS question.
|
||||
*/
|
||||
public final class DnsQuestion extends DnsEntry {
|
||||
|
||||
public interface DnsQuestion extends DnsRecord {
|
||||
/**
|
||||
* Constructs a question with the default class IN (Internet).
|
||||
*
|
||||
* @param name
|
||||
* the domain name being queried i.e. "www.example.com"
|
||||
* @param type
|
||||
* the question type, which represents the type of
|
||||
* {@link DnsResource} record that should be returned
|
||||
* An unused property. This method will always return {@code 0}.
|
||||
*/
|
||||
public DnsQuestion(String name, DnsType type) {
|
||||
this(name, type, IN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a question with the given class.
|
||||
*
|
||||
* @param name
|
||||
* the domain name being queried i.e. "www.example.com"
|
||||
* @param type
|
||||
* the question type, which represents the type of
|
||||
* {@link DnsResource} record that should be returned
|
||||
* @param qClass
|
||||
* the class of a DNS record
|
||||
*/
|
||||
public DnsQuestion(String name, DnsType type, DnsClass qClass) {
|
||||
super(name, type, qClass);
|
||||
|
||||
if (name.isEmpty()) {
|
||||
throw new IllegalArgumentException("name must not be left blank.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof DnsQuestion)) {
|
||||
return false;
|
||||
}
|
||||
return super.equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
long timeToLive();
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.buffer.ByteBufHolder;
|
||||
|
||||
/**
|
||||
* A generic {@link DnsRecord} that contains an undecoded {@code RDATA}.
|
||||
*/
|
||||
public interface DnsRawRecord extends DnsRecord, ByteBufHolder {
|
||||
@Override
|
||||
DnsRawRecord copy();
|
||||
|
||||
@Override
|
||||
DnsRawRecord duplicate();
|
||||
|
||||
@Override
|
||||
DnsRawRecord retain();
|
||||
|
||||
@Override
|
||||
DnsRawRecord retain(int increment);
|
||||
|
||||
@Override
|
||||
DnsRawRecord touch();
|
||||
|
||||
@Override
|
||||
DnsRawRecord touch(Object hint);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A DNS resource record.
|
||||
*/
|
||||
public interface DnsRecord {
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code IN}
|
||||
*/
|
||||
int CLASS_IN = 0x0001;
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code CSNET}
|
||||
*/
|
||||
int CLASS_CSNET = 0x0002;
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code CHAOS}
|
||||
*/
|
||||
int CLASS_CHAOS = 0x0003;
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code HESIOD}
|
||||
*/
|
||||
int CLASS_HESIOD = 0x0004;
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code NONE}
|
||||
*/
|
||||
int CLASS_NONE = 0x00fe;
|
||||
|
||||
/**
|
||||
* DNS resource record class: {@code ANY}
|
||||
*/
|
||||
int CLASS_ANY = 0x00ff;
|
||||
|
||||
/**
|
||||
* Returns the name of this resource record.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Returns the type of this resource record.
|
||||
*/
|
||||
DnsRecordType type();
|
||||
|
||||
/**
|
||||
* Returns the class of this resource record.
|
||||
*
|
||||
* @return the class value, 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>
|
||||
*/
|
||||
int dnsClass();
|
||||
|
||||
/**
|
||||
* Returns the time to live after reading for this resource record.
|
||||
*/
|
||||
long timeToLive();
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* Decodes a DNS record into its object representation.
|
||||
*
|
||||
* @see DatagramDnsResponseDecoder
|
||||
*/
|
||||
public interface DnsRecordDecoder {
|
||||
|
||||
DnsRecordDecoder DEFAULT = new DefaultDnsRecordDecoder();
|
||||
|
||||
/**
|
||||
* Decodes a DNS question into its object representation.
|
||||
*
|
||||
* @param in the input buffer which contains a DNS question at its reader index
|
||||
*/
|
||||
DnsQuestion decodeQuestion(ByteBuf in) throws Exception;
|
||||
|
||||
/**
|
||||
* Decodes a DNS record into its object representation.
|
||||
*
|
||||
* @param in the input buffer which contains a DNS record at its reader index
|
||||
*/
|
||||
<T extends DnsRecord> T decodeRecord(ByteBuf in) throws Exception;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* Encodes a {@link DnsRecord} into binary representation.
|
||||
*
|
||||
* @see DatagramDnsQueryEncoder
|
||||
*/
|
||||
public interface DnsRecordEncoder {
|
||||
|
||||
DnsRecordEncoder DEFAULT = new DefaultDnsRecordEncoder();
|
||||
|
||||
/**
|
||||
* Encodes a {@link DnsQuestion}.
|
||||
*
|
||||
* @param out the output buffer where the encoded question will be written to
|
||||
*/
|
||||
void encodeQuestion(DnsQuestion question, ByteBuf out) throws Exception;
|
||||
|
||||
/**
|
||||
* Encodes a {@link DnsRecord}.
|
||||
*
|
||||
* @param out the output buffer where the encoded record will be written to
|
||||
*/
|
||||
void encodeRecord(DnsRecord record, ByteBuf out) throws Exception;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
* 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
|
||||
@ -16,32 +16,33 @@
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a DNS record type.
|
||||
*/
|
||||
public final class DnsType implements Comparable<DnsType> {
|
||||
public class DnsRecordType implements Comparable<DnsRecordType> {
|
||||
|
||||
/**
|
||||
* Address record RFC 1035 Returns a 32-bit IPv4 address, most commonly used
|
||||
* to map hostnames to an IP address of the host, but also used for DNSBLs,
|
||||
* storing subnet masks in RFC 1101, etc.
|
||||
*/
|
||||
public static final DnsType A = new DnsType(0x0001, "A");
|
||||
public static final DnsRecordType A = new DnsRecordType(0x0001, "A");
|
||||
|
||||
/**
|
||||
* Name server record RFC 1035 Delegates a DNS zone to use the given
|
||||
* authoritative name servers
|
||||
*/
|
||||
public static final DnsType NS = new DnsType(0x0002, "NS");
|
||||
public static final DnsRecordType NS = new DnsRecordType(0x0002, "NS");
|
||||
|
||||
/**
|
||||
* Canonical name record RFC 1035 Alias of one name to another: the DNS
|
||||
* lookup will continue by retrying the lookup with the new name.
|
||||
*/
|
||||
public static final DnsType CNAME = new DnsType(0x0005, "CNAME");
|
||||
public static final DnsRecordType CNAME = new DnsRecordType(0x0005, "CNAME");
|
||||
|
||||
/**
|
||||
* Start of [a zone of] authority record RFC 1035 and RFC 2308 Specifies
|
||||
@ -49,7 +50,7 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* server, the email of the domain administrator, the domain serial number,
|
||||
* and several timers relating to refreshing the zone.
|
||||
*/
|
||||
public static final DnsType SOA = new DnsType(0x0006, "SOA");
|
||||
public static final DnsRecordType SOA = new DnsRecordType(0x0006, "SOA");
|
||||
|
||||
/**
|
||||
* Pointer record RFC 1035 Pointer to a canonical name. Unlike a CNAME, DNS
|
||||
@ -57,13 +58,13 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* use is for implementing reverse DNS lookups, but other uses include such
|
||||
* things as DNS-SD.
|
||||
*/
|
||||
public static final DnsType PTR = new DnsType(0x000c, "PTR");
|
||||
public static final DnsRecordType PTR = new DnsRecordType(0x000c, "PTR");
|
||||
|
||||
/**
|
||||
* Mail exchange record RFC 1035 Maps a domain name to a list of message
|
||||
* transfer agents for that domain.
|
||||
*/
|
||||
public static final DnsType MX = new DnsType(0x000f, "MX");
|
||||
public static final DnsRecordType MX = new DnsRecordType(0x000f, "MX");
|
||||
|
||||
/**
|
||||
* Text record RFC 1035 Originally for arbitrary human-readable text in a
|
||||
@ -72,14 +73,14 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* opportunistic encryption, Sender Policy Framework, DKIM, DMARC DNS-SD,
|
||||
* etc.
|
||||
*/
|
||||
public static final DnsType TXT = new DnsType(0x0010, "TXT");
|
||||
public static final DnsRecordType TXT = new DnsRecordType(0x0010, "TXT");
|
||||
|
||||
/**
|
||||
* Responsible person record RFC 1183 Information about the responsible
|
||||
* person(s) for the domain. Usually an email address with the @ replaced by
|
||||
* a .
|
||||
*/
|
||||
public static final DnsType RP = new DnsType(0x0011, "RP");
|
||||
public static final DnsRecordType RP = new DnsRecordType(0x0011, "RP");
|
||||
|
||||
/**
|
||||
* AFS database record RFC 1183 Location of database servers of an AFS cell.
|
||||
@ -87,14 +88,14 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* their local domain. A subtype of this record is used by the obsolete
|
||||
* DCE/DFS file system.
|
||||
*/
|
||||
public static final DnsType AFSDB = new DnsType(0x0012, "AFSDB");
|
||||
public static final DnsRecordType AFSDB = new DnsRecordType(0x0012, "AFSDB");
|
||||
|
||||
/**
|
||||
* Signature record RFC 2535 Signature record used in SIG(0) (RFC 2931) and
|
||||
* TKEY (RFC 2930). RFC 3755 designated RRSIG as the replacement for SIG for
|
||||
* use within DNSSEC.
|
||||
*/
|
||||
public static final DnsType SIG = new DnsType(0x0018, "SIG");
|
||||
public static final DnsRecordType SIG = new DnsRecordType(0x0018, "SIG");
|
||||
|
||||
/**
|
||||
* key record RFC 2535 and RFC 2930 Used only for SIG(0) (RFC 2931) and TKEY
|
||||
@ -103,32 +104,32 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* replacement within DNSSEC. RFC 4025 designates IPSECKEY as the
|
||||
* replacement for use with IPsec.
|
||||
*/
|
||||
public static final DnsType KEY = new DnsType(0x0019, "KEY");
|
||||
public static final DnsRecordType KEY = new DnsRecordType(0x0019, "KEY");
|
||||
|
||||
/**
|
||||
* IPv6 address record RFC 3596 Returns a 128-bit IPv6 address, most
|
||||
* commonly used to map hostnames to an IP address of the host.
|
||||
*/
|
||||
public static final DnsType AAAA = new DnsType(0x001c, "AAAA");
|
||||
public static final DnsRecordType AAAA = new DnsRecordType(0x001c, "AAAA");
|
||||
|
||||
/**
|
||||
* Location record RFC 1876 Specifies a geographical location associated
|
||||
* with a domain name.
|
||||
*/
|
||||
public static final DnsType LOC = new DnsType(0x001d, "LOC");
|
||||
public static final DnsRecordType LOC = new DnsRecordType(0x001d, "LOC");
|
||||
|
||||
/**
|
||||
* Service locator RFC 2782 Generalized service location record, used for
|
||||
* newer protocols instead of creating protocol-specific records such as MX.
|
||||
*/
|
||||
public static final DnsType SRV = new DnsType(0x0021, "SRV");
|
||||
public static final DnsRecordType SRV = new DnsRecordType(0x0021, "SRV");
|
||||
|
||||
/**
|
||||
* Naming Authority Pointer record RFC 3403 Allows regular expression based
|
||||
* rewriting of domain names which can then be used as URIs, further domain
|
||||
* names to lookups, etc.
|
||||
*/
|
||||
public static final DnsType NAPTR = new DnsType(0x0023, "NAPTR");
|
||||
public static final DnsRecordType NAPTR = new DnsRecordType(0x0023, "NAPTR");
|
||||
|
||||
/**
|
||||
* Key eXchanger record RFC 2230 Used with some cryptographic systems (not
|
||||
@ -137,12 +138,12 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* Informational status, rather than being on the IETF standards-track. It
|
||||
* has always had limited deployment, but is still in use.
|
||||
*/
|
||||
public static final DnsType KX = new DnsType(0x0024, "KX");
|
||||
public static final DnsRecordType KX = new DnsRecordType(0x0024, "KX");
|
||||
|
||||
/**
|
||||
* Certificate record RFC 4398 Stores PKIX, SPKI, PGP, etc.
|
||||
*/
|
||||
public static final DnsType CERT = new DnsType(0x0025, "CERT");
|
||||
public static final DnsRecordType CERT = new DnsRecordType(0x0025, "CERT");
|
||||
|
||||
/**
|
||||
* Delegation name record RFC 2672 DNAME creates an alias for a name and all
|
||||
@ -150,25 +151,25 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* label. Like the CNAME record, the DNS lookup will continue by retrying
|
||||
* the lookup with the new name.
|
||||
*/
|
||||
public static final DnsType DNAME = new DnsType(0x0027, "DNAME");
|
||||
public static final DnsRecordType DNAME = new DnsRecordType(0x0027, "DNAME");
|
||||
|
||||
/**
|
||||
* Option record RFC 2671 This is a pseudo DNS record type needed to support
|
||||
* EDNS.
|
||||
*/
|
||||
public static final DnsType OPT = new DnsType(0x0029, "OPT");
|
||||
public static final DnsRecordType OPT = new DnsRecordType(0x0029, "OPT");
|
||||
|
||||
/**
|
||||
* Address Prefix List record RFC 3123 Specify lists of address ranges, e.g.
|
||||
* in CIDR format, for various address families. Experimental.
|
||||
*/
|
||||
public static final DnsType APL = new DnsType(0x002a, "APL");
|
||||
public static final DnsRecordType APL = new DnsRecordType(0x002a, "APL");
|
||||
|
||||
/**
|
||||
* Delegation signer record RFC 4034 The record used to identify the DNSSEC
|
||||
* signing key of a delegated zone.
|
||||
*/
|
||||
public static final DnsType DS = new DnsType(0x002b, "DS");
|
||||
public static final DnsRecordType DS = new DnsRecordType(0x002b, "DS");
|
||||
|
||||
/**
|
||||
* SSH Public Key Fingerprint record RFC 4255 Resource record for publishing
|
||||
@ -176,47 +177,47 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* verifying the authenticity of the host. RFC 6594 defines ECC SSH keys and
|
||||
* SHA-256 hashes. See the IANA SSHFP RR parameters registry for details.
|
||||
*/
|
||||
public static final DnsType SSHFP = new DnsType(0x002c, "SSHFP");
|
||||
public static final DnsRecordType SSHFP = new DnsRecordType(0x002c, "SSHFP");
|
||||
|
||||
/**
|
||||
* IPsec Key record RFC 4025 Key record that can be used with IPsec.
|
||||
*/
|
||||
public static final DnsType IPSECKEY = new DnsType(0x002d, "IPSECKEY");
|
||||
public static final DnsRecordType IPSECKEY = new DnsRecordType(0x002d, "IPSECKEY");
|
||||
|
||||
/**
|
||||
* DNSSEC signature record RFC 4034 Signature for a DNSSEC-secured record
|
||||
* set. Uses the same format as the SIG record.
|
||||
*/
|
||||
public static final DnsType RRSIG = new DnsType(0x002e, "RRSIG");
|
||||
public static final DnsRecordType RRSIG = new DnsRecordType(0x002e, "RRSIG");
|
||||
|
||||
/**
|
||||
* Next-Secure record RFC 4034 Part of DNSSEC, used to prove a name does not
|
||||
* exist. Uses the same format as the (obsolete) NXT record.
|
||||
*/
|
||||
public static final DnsType NSEC = new DnsType(0x002f, "NSEC");
|
||||
public static final DnsRecordType NSEC = new DnsRecordType(0x002f, "NSEC");
|
||||
|
||||
/**
|
||||
* DNS Key record RFC 4034 The key record used in DNSSEC. Uses the same
|
||||
* format as the KEY record.
|
||||
*/
|
||||
public static final DnsType DNSKEY = new DnsType(0x0030, "DNSKEY");
|
||||
public static final DnsRecordType DNSKEY = new DnsRecordType(0x0030, "DNSKEY");
|
||||
|
||||
/**
|
||||
* DHCP identifier record RFC 4701 Used in conjunction with the FQDN option
|
||||
* to DHCP.
|
||||
*/
|
||||
public static final DnsType DHCID = new DnsType(0x0031, "DHCID");
|
||||
public static final DnsRecordType DHCID = new DnsRecordType(0x0031, "DHCID");
|
||||
|
||||
/**
|
||||
* NSEC record version 3 RFC 5155 An extension to DNSSEC that allows proof
|
||||
* of nonexistence for a name without permitting zonewalking.
|
||||
*/
|
||||
public static final DnsType NSEC3 = new DnsType(0x0032, "NSEC3");
|
||||
public static final DnsRecordType NSEC3 = new DnsRecordType(0x0032, "NSEC3");
|
||||
|
||||
/**
|
||||
* NSEC3 parameters record RFC 5155 Parameter record for use with NSEC3.
|
||||
*/
|
||||
public static final DnsType NSEC3PARAM = new DnsType(0x0033, "NSEC3PARAM");
|
||||
public static final DnsRecordType NSEC3PARAM = new DnsRecordType(0x0033, "NSEC3PARAM");
|
||||
|
||||
/**
|
||||
* TLSA certificate association record RFC 6698 A record for DNS-based
|
||||
@ -225,34 +226,34 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* key with the domain name where the record is found, thus forming a 'TLSA
|
||||
* certificate association'.
|
||||
*/
|
||||
public static final DnsType TLSA = new DnsType(0x0034, "TLSA");
|
||||
public static final DnsRecordType TLSA = new DnsRecordType(0x0034, "TLSA");
|
||||
|
||||
/**
|
||||
* Host Identity Protocol record RFC 5205 Method of separating the end-point
|
||||
* identifier and locator roles of IP addresses.
|
||||
*/
|
||||
public static final DnsType HIP = new DnsType(0x0037, "HIP");
|
||||
public static final DnsRecordType HIP = new DnsRecordType(0x0037, "HIP");
|
||||
|
||||
/**
|
||||
* Sender Policy Framework record RFC 4408 Specified as part of the SPF
|
||||
* protocol as an alternative to of storing SPF data in TXT records. Uses
|
||||
* the same format as the earlier TXT record.
|
||||
*/
|
||||
public static final DnsType SPF = new DnsType(0x0063, "SPF");
|
||||
public static final DnsRecordType SPF = new DnsRecordType(0x0063, "SPF");
|
||||
|
||||
/**
|
||||
* Secret key record RFC 2930 A method of providing keying material to be
|
||||
* used with TSIG that is encrypted under the public key in an accompanying
|
||||
* KEY RR..
|
||||
*/
|
||||
public static final DnsType TKEY = new DnsType(0x00f9, "TKEY");
|
||||
public static final DnsRecordType TKEY = new DnsRecordType(0x00f9, "TKEY");
|
||||
|
||||
/**
|
||||
* Transaction Signature record RFC 2845 Can be used to authenticate dynamic
|
||||
* updates as coming from an approved client, or to authenticate responses
|
||||
* as coming from an approved recursive name server similar to DNSSEC.
|
||||
*/
|
||||
public static final DnsType TSIG = new DnsType(0x00fa, "TSIG");
|
||||
public static final DnsRecordType TSIG = new DnsRecordType(0x00fa, "TSIG");
|
||||
|
||||
/**
|
||||
* Incremental Zone Transfer record RFC 1996 Requests a zone transfer of the
|
||||
@ -261,13 +262,13 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* authoritative server is unable to fulfill the request due to
|
||||
* configuration or lack of required deltas.
|
||||
*/
|
||||
public static final DnsType IXFR = new DnsType(0x00fb, "IXFR");
|
||||
public static final DnsRecordType IXFR = new DnsRecordType(0x00fb, "IXFR");
|
||||
|
||||
/**
|
||||
* Authoritative Zone Transfer record RFC 1035 Transfer entire zone file
|
||||
* from the master name server to secondary name servers.
|
||||
*/
|
||||
public static final DnsType AXFR = new DnsType(0x00fc, "AXFR");
|
||||
public static final DnsRecordType AXFR = new DnsRecordType(0x00fc, "AXFR");
|
||||
|
||||
/**
|
||||
* All cached records RFC 1035 Returns all records of all types known to the
|
||||
@ -278,49 +279,50 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
* returned. Sometimes referred to as ANY, for example in Windows nslookup
|
||||
* and Wireshark.
|
||||
*/
|
||||
public static final DnsType ANY = new DnsType(0x00ff, "ANY");
|
||||
public static final DnsRecordType ANY = new DnsRecordType(0x00ff, "ANY");
|
||||
|
||||
/**
|
||||
* Certification Authority Authorization record RFC 6844 CA pinning,
|
||||
* constraining acceptable CAs for a host/domain.
|
||||
*/
|
||||
public static final DnsType CAA = new DnsType(0x0101, "CAA");
|
||||
public static final DnsRecordType CAA = new DnsRecordType(0x0101, "CAA");
|
||||
|
||||
/**
|
||||
* DNSSEC Trust Authorities record N/A Part of a deployment proposal for
|
||||
* DNSSEC without a signed DNS root. See the IANA database and Weiler Spec
|
||||
* for details. Uses the same format as the DS record.
|
||||
*/
|
||||
public static final DnsType TA = new DnsType(0x8000, "TA");
|
||||
public static final DnsRecordType TA = new DnsRecordType(0x8000, "TA");
|
||||
|
||||
/**
|
||||
* DNSSEC Lookaside Validation record RFC 4431 For publishing DNSSEC trust
|
||||
* anchors outside of the DNS delegation chain. Uses the same format as the
|
||||
* DS record. RFC 5074 describes a way of using these records.
|
||||
*/
|
||||
public static final DnsType DLV = new DnsType(0x8001, "DLV");
|
||||
public static final DnsRecordType DLV = new DnsRecordType(0x8001, "DLV");
|
||||
|
||||
private static final Map<String, DnsType> BY_NAME = new HashMap<String, DnsType>();
|
||||
private static final IntObjectHashMap<DnsType> BY_TYPE = new IntObjectHashMap<DnsType>();
|
||||
private static final Map<String, DnsRecordType> BY_NAME = new HashMap<String, DnsRecordType>();
|
||||
private static final IntObjectHashMap<DnsRecordType> BY_TYPE = new IntObjectHashMap<DnsRecordType>();
|
||||
private static final String EXPECTED;
|
||||
|
||||
static {
|
||||
DnsType[] all = {
|
||||
DnsRecordType[] all = {
|
||||
A, NS, CNAME, SOA, PTR, MX, TXT, RP, AFSDB, SIG, KEY, AAAA, LOC, SRV, NAPTR, KX, CERT, DNAME, OPT, APL,
|
||||
DS, SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, HIP, SPF, TKEY, TSIG, IXFR,
|
||||
AXFR, ANY, CAA, TA, DLV
|
||||
};
|
||||
|
||||
StringBuilder expected = new StringBuilder(512);
|
||||
expected.append(" (expected: ");
|
||||
final StringBuilder expected = new StringBuilder(512);
|
||||
|
||||
for (DnsType type: all) {
|
||||
expected.append(" (expected: ");
|
||||
for (DnsRecordType type: all) {
|
||||
BY_NAME.put(type.name(), type);
|
||||
BY_TYPE.put(type.intValue(), type);
|
||||
expected.append(type.name());
|
||||
expected.append('(');
|
||||
expected.append(type.intValue());
|
||||
expected.append("), ");
|
||||
|
||||
expected.append(type.name())
|
||||
.append('(')
|
||||
.append(type.intValue())
|
||||
.append("), ");
|
||||
}
|
||||
|
||||
expected.setLength(expected.length() - 2);
|
||||
@ -328,33 +330,31 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
EXPECTED = expected.toString();
|
||||
}
|
||||
|
||||
public static DnsType valueOf(int intValue) {
|
||||
DnsType result = BY_TYPE.get(intValue);
|
||||
public static DnsRecordType valueOf(int intValue) {
|
||||
DnsRecordType result = BY_TYPE.get(intValue);
|
||||
if (result == null) {
|
||||
return new DnsType(intValue, "UNKNOWN");
|
||||
return new DnsRecordType(intValue);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static DnsType valueOf(String name) {
|
||||
DnsType result = BY_NAME.get(name);
|
||||
public static DnsRecordType valueOf(String name) {
|
||||
DnsRecordType result = BY_NAME.get(name);
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException("name: " + name + EXPECTED);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance.
|
||||
*/
|
||||
public static DnsType valueOf(int intValue, String name) {
|
||||
return new DnsType(intValue, name);
|
||||
}
|
||||
|
||||
private final int intValue;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
private DnsType(int intValue, String name) {
|
||||
private DnsRecordType(int intValue) {
|
||||
this(intValue, "UNKNOWN");
|
||||
}
|
||||
|
||||
public DnsRecordType(int intValue, String name) {
|
||||
if ((intValue & 0xffff) != intValue) {
|
||||
throw new IllegalArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)");
|
||||
}
|
||||
@ -383,16 +383,20 @@ public final class DnsType implements Comparable<DnsType> {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof DnsType && ((DnsType) o).intValue == intValue;
|
||||
return o instanceof DnsRecordType && ((DnsRecordType) o).intValue == intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DnsType o) {
|
||||
public int compareTo(DnsRecordType o) {
|
||||
return intValue() - o.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + intValue() + ')';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
|
||||
/**
|
||||
* Represents any resource record (answer, authority, or additional resource
|
||||
* records).
|
||||
*/
|
||||
public final class DnsResource extends DnsEntry implements ByteBufHolder {
|
||||
|
||||
private final long ttl;
|
||||
private final ByteBuf content;
|
||||
|
||||
/**
|
||||
* Constructs a resource record.
|
||||
*
|
||||
* @param name
|
||||
* the domain name
|
||||
* @param type
|
||||
* the type of record being returned
|
||||
* @param aClass
|
||||
* the class for this resource record
|
||||
* @param ttl
|
||||
* the time to live after reading
|
||||
* @param content
|
||||
* the data contained in this record
|
||||
*/
|
||||
public DnsResource(String name, DnsType type, DnsClass aClass, long ttl, ByteBuf content) {
|
||||
super(name, type, aClass);
|
||||
this.ttl = ttl;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time to live after reading for this resource record.
|
||||
*/
|
||||
public long timeToLive() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data contained in this resource record.
|
||||
*/
|
||||
@Override
|
||||
public ByteBuf content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of this resource record.
|
||||
*/
|
||||
@Override
|
||||
public DnsResource copy() {
|
||||
return new DnsResource(name(), type(), dnsClass(), ttl, content.copy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a duplicate of this resource record.
|
||||
*/
|
||||
@Override
|
||||
public DnsResource duplicate() {
|
||||
return new DnsResource(name(), type(), dnsClass(), ttl, content.duplicate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int refCnt() {
|
||||
return content.refCnt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResource retain() {
|
||||
content.retain();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResource retain(int increment) {
|
||||
content.retain(increment);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
return content.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release(int decrement) {
|
||||
return content.release(decrement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResource touch() {
|
||||
content.touch();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResource touch(Object hint) {
|
||||
content.touch(hint);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* 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
|
||||
@ -15,86 +15,99 @@
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
/**
|
||||
* A DNS response message.
|
||||
*/
|
||||
public interface DnsResponse extends DnsMessage {
|
||||
|
||||
/**
|
||||
* A DNS response packet which is sent to a client after a server receives a
|
||||
* query.
|
||||
* Returns {@code true} if responding server is authoritative for the domain
|
||||
* name in the query message.
|
||||
*/
|
||||
public final class DnsResponse extends DnsMessage {
|
||||
|
||||
private final InetSocketAddress sender;
|
||||
|
||||
public DnsResponse(int id, InetSocketAddress sender) {
|
||||
super(id);
|
||||
if (sender == null) {
|
||||
throw new NullPointerException("sender");
|
||||
}
|
||||
this.sender = sender;
|
||||
}
|
||||
boolean isAuthoritativeAnswer();
|
||||
|
||||
/**
|
||||
* The {@link InetSocketAddress} of the sender of this {@link DnsResponse}
|
||||
* Set to {@code true} if responding server is authoritative for the domain
|
||||
* name in the query message.
|
||||
*
|
||||
* @param authoritativeAnswer flag for authoritative answer
|
||||
*/
|
||||
public InetSocketAddress sender() {
|
||||
return sender;
|
||||
}
|
||||
DnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if response has been truncated, usually if it is
|
||||
* over 512 bytes.
|
||||
*/
|
||||
boolean isTruncated();
|
||||
|
||||
/**
|
||||
* Set to {@code true} if response has been truncated (usually happens for
|
||||
* responses over 512 bytes).
|
||||
*
|
||||
* @param truncated flag for truncation
|
||||
*/
|
||||
DnsResponse setTruncated(boolean truncated);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if DNS server can handle recursive queries.
|
||||
*/
|
||||
boolean isRecursionAvailable();
|
||||
|
||||
/**
|
||||
* Set to {@code true} if DNS server can handle recursive queries.
|
||||
*
|
||||
* @param recursionAvailable flag for recursion availability
|
||||
*/
|
||||
DnsResponse setRecursionAvailable(boolean recursionAvailable);
|
||||
|
||||
/**
|
||||
* Returns the 4 bit return code.
|
||||
*/
|
||||
DnsResponseCode code();
|
||||
|
||||
/**
|
||||
* Sets the response code for this message.
|
||||
*
|
||||
* @param code the response code
|
||||
*/
|
||||
DnsResponse setCode(DnsResponseCode code);
|
||||
|
||||
@Override
|
||||
public DnsResponse addAnswer(DnsResource answer) {
|
||||
super.addAnswer(answer);
|
||||
return this;
|
||||
}
|
||||
DnsResponse setId(int id);
|
||||
|
||||
@Override
|
||||
public DnsResponse addQuestion(DnsQuestion question) {
|
||||
super.addQuestion(question);
|
||||
return this;
|
||||
}
|
||||
DnsResponse setOpCode(DnsOpCode opCode);
|
||||
|
||||
@Override
|
||||
public DnsResponse addAuthorityResource(DnsResource resource) {
|
||||
super.addAuthorityResource(resource);
|
||||
return this;
|
||||
}
|
||||
DnsResponse setRecursionDesired(boolean recursionDesired);
|
||||
|
||||
@Override
|
||||
public DnsResponse addAdditionalResource(DnsResource resource) {
|
||||
super.addAdditionalResource(resource);
|
||||
return this;
|
||||
}
|
||||
DnsResponse setZ(int z);
|
||||
|
||||
@Override
|
||||
public DnsResponse touch(Object hint) {
|
||||
super.touch(hint);
|
||||
return this;
|
||||
}
|
||||
DnsResponse setRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsResponse retain() {
|
||||
super.retain();
|
||||
return this;
|
||||
}
|
||||
DnsResponse addRecord(DnsSection section, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsResponse retain(int increment) {
|
||||
super.retain(increment);
|
||||
return this;
|
||||
}
|
||||
DnsResponse addRecord(DnsSection section, int index, DnsRecord record);
|
||||
|
||||
@Override
|
||||
public DnsResponse touch() {
|
||||
super.touch();
|
||||
return this;
|
||||
}
|
||||
DnsResponse clear(DnsSection section);
|
||||
|
||||
@Override
|
||||
public DnsResponseHeader header() {
|
||||
return (DnsResponseHeader) super.header();
|
||||
}
|
||||
DnsResponse clear();
|
||||
|
||||
@Override
|
||||
protected DnsResponseHeader newHeader(int id) {
|
||||
return new DnsResponseHeader(this, id);
|
||||
}
|
||||
DnsResponse touch();
|
||||
|
||||
@Override
|
||||
DnsResponse touch(Object hint);
|
||||
|
||||
@Override
|
||||
DnsResponse retain();
|
||||
|
||||
@Override
|
||||
DnsResponse retain(int increment);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 The Netty Project
|
||||
* 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
|
||||
@ -15,110 +15,118 @@
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* Represents the possible response codes a server may send after receiving a
|
||||
* query. A response code of 0 indicates no error.
|
||||
* The DNS {@code RCODE}, as defined in <a href="https://tools.ietf.org/html/rfc2929">RFC2929</a>.
|
||||
*/
|
||||
public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
||||
public class DnsResponseCode implements Comparable<DnsResponseCode> {
|
||||
|
||||
/**
|
||||
* ID 0, no error
|
||||
* The 'NoError' DNS RCODE (0), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NOERROR = new DnsResponseCode(0, "no error");
|
||||
public static final DnsResponseCode NOERROR = new DnsResponseCode(0, "NoError");
|
||||
|
||||
/**
|
||||
* ID 1, format error
|
||||
* The 'FormErr' DNS RCODE (1), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode FORMERROR = new DnsResponseCode(1, "format error");
|
||||
public static final DnsResponseCode FORMERR = new DnsResponseCode(1, "FormErr");
|
||||
|
||||
/**
|
||||
* ID 2, server failure
|
||||
* The 'ServFail' DNS RCODE (2), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode SERVFAIL = new DnsResponseCode(2, "server failure");
|
||||
public static final DnsResponseCode SERVFAIL = new DnsResponseCode(2, "ServFail");
|
||||
|
||||
/**
|
||||
* ID 3, name error
|
||||
* The 'NXDomain' DNS RCODE (3), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "name error");
|
||||
public static final DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "NXDomain");
|
||||
|
||||
/**
|
||||
* ID 4, not implemented
|
||||
* The 'NotImp' DNS RCODE (4), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NOTIMPL = new DnsResponseCode(4, "not implemented");
|
||||
public static final DnsResponseCode NOTIMP = new DnsResponseCode(4, "NotImp");
|
||||
|
||||
/**
|
||||
* ID 5, operation refused
|
||||
* The 'Refused' DNS RCODE (5), as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC1035</a>.
|
||||
*/
|
||||
public static final DnsResponseCode REFUSED = new DnsResponseCode(5, "operation refused");
|
||||
public static final DnsResponseCode REFUSED = new DnsResponseCode(5, "Refused");
|
||||
|
||||
/**
|
||||
* ID 6, domain name should not exist
|
||||
* The 'YXDomain' DNS RCODE (6), as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsResponseCode YXDOMAIN = new DnsResponseCode(6, "domain name should not exist");
|
||||
public static final DnsResponseCode YXDOMAIN = new DnsResponseCode(6, "YXDomain");
|
||||
|
||||
/**
|
||||
* ID 7, resource record set should not exist
|
||||
* The 'YXRRSet' DNS RCODE (7), as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsResponseCode YXRRSET = new DnsResponseCode(7, "resource record set should not exist");
|
||||
public static final DnsResponseCode YXRRSET = new DnsResponseCode(7, "YXRRSet");
|
||||
|
||||
/**
|
||||
* ID 8, rrset does not exist
|
||||
* The 'NXRRSet' DNS RCODE (8), as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NXRRSET = new DnsResponseCode(8, "rrset does not exist");
|
||||
public static final DnsResponseCode NXRRSET = new DnsResponseCode(8, "NXRRSet");
|
||||
|
||||
/**
|
||||
* ID 9, not authoritative for zone
|
||||
* The 'NotAuth' DNS RCODE (9), as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NOTAUTH = new DnsResponseCode(9, "not authoritative for zone");
|
||||
public static final DnsResponseCode NOTAUTH = new DnsResponseCode(9, "NotAuth");
|
||||
|
||||
/**
|
||||
* ID 10, name not in zone
|
||||
* The 'NotZone' DNS RCODE (10), as defined in <a href="https://tools.ietf.org/html/rfc2136">RFC2136</a>.
|
||||
*/
|
||||
public static final DnsResponseCode NOTZONE = new DnsResponseCode(10, "name not in zone");
|
||||
public static final DnsResponseCode NOTZONE = new DnsResponseCode(10, "NotZone");
|
||||
|
||||
/**
|
||||
* ID 11, bad extension mechanism for version
|
||||
* The 'BADVERS' or 'BADSIG' DNS RCODE (16), as defined in <a href="https://tools.ietf.org/html/rfc2671">RFC2671</a>
|
||||
* and <a href="https://tools.ietf.org/html/rfc2845">RFC2845</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADVERS = new DnsResponseCode(11, "bad extension mechanism for version");
|
||||
public static final DnsResponseCode BADVERS_OR_BADSIG = new DnsResponseCode(16, "BADVERS_OR_BADSIG");
|
||||
|
||||
/**
|
||||
* ID 12, bad signature
|
||||
* The 'BADKEY' DNS RCODE (17), as defined in <a href="https://tools.ietf.org/html/rfc2845">RFC2845</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADSIG = new DnsResponseCode(12, "bad signature");
|
||||
public static final DnsResponseCode BADKEY = new DnsResponseCode(17, "BADKEY");
|
||||
|
||||
/**
|
||||
* ID 13, bad key
|
||||
* The 'BADTIME' DNS RCODE (18), as defined in <a href="https://tools.ietf.org/html/rfc2845">RFC2845</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADKEY = new DnsResponseCode(13, "bad key");
|
||||
public static final DnsResponseCode BADTIME = new DnsResponseCode(18, "BADTIME");
|
||||
|
||||
/**
|
||||
* ID 14, bad timestamp
|
||||
* The 'BADMODE' DNS RCODE (19), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADTIME = new DnsResponseCode(14, "bad timestamp");
|
||||
|
||||
private final int errorCode;
|
||||
private final String message;
|
||||
public static final DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE");
|
||||
|
||||
/**
|
||||
* Returns the {@link DnsResponseCode} that corresponds with the given
|
||||
* {@code responseCode}.
|
||||
* The 'BADNAME' DNS RCODE (20), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADNAME = new DnsResponseCode(20, "BADNAME");
|
||||
|
||||
/**
|
||||
* The 'BADALG' DNS RCODE (21), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
|
||||
*/
|
||||
public static final DnsResponseCode BADALG = new DnsResponseCode(21, "BADALG");
|
||||
|
||||
/**
|
||||
* Returns the {@link DnsResponseCode} that corresponds with the given {@code responseCode}.
|
||||
*
|
||||
* @param responseCode
|
||||
* the error code's id
|
||||
* @return corresponding {@link DnsResponseCode} or {@code null} if none can be found.
|
||||
* @param responseCode the DNS RCODE
|
||||
*
|
||||
* @return the corresponding {@link DnsResponseCode}
|
||||
*/
|
||||
public static DnsResponseCode valueOf(int responseCode) {
|
||||
switch (responseCode) {
|
||||
case 0:
|
||||
return NOERROR;
|
||||
case 1:
|
||||
return FORMERROR;
|
||||
return FORMERR;
|
||||
case 2:
|
||||
return SERVFAIL;
|
||||
case 3:
|
||||
return NXDOMAIN;
|
||||
case 4:
|
||||
return NOTIMPL;
|
||||
return NOTIMP;
|
||||
case 5:
|
||||
return REFUSED;
|
||||
case 6:
|
||||
@ -131,43 +139,59 @@ public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
||||
return NOTAUTH;
|
||||
case 10:
|
||||
return NOTZONE;
|
||||
case 11:
|
||||
return BADVERS;
|
||||
case 12:
|
||||
return BADSIG;
|
||||
case 13:
|
||||
case 16:
|
||||
return BADVERS_OR_BADSIG;
|
||||
case 17:
|
||||
return BADKEY;
|
||||
case 14:
|
||||
case 18:
|
||||
return BADTIME;
|
||||
case 19:
|
||||
return BADMODE;
|
||||
case 20:
|
||||
return BADNAME;
|
||||
case 21:
|
||||
return BADALG;
|
||||
default:
|
||||
return new DnsResponseCode(responseCode, null);
|
||||
return new DnsResponseCode(responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
public DnsResponseCode(int errorCode, String message) {
|
||||
this.errorCode = errorCode;
|
||||
this.message = message;
|
||||
private final int code;
|
||||
private final String name;
|
||||
private String text;
|
||||
|
||||
private DnsResponseCode(int code) {
|
||||
this(code, "UNKNOWN");
|
||||
}
|
||||
|
||||
public DnsResponseCode(int code, String name) {
|
||||
if (code < 0 || code > 65535) {
|
||||
throw new IllegalArgumentException("code: " + code + " (expected: 0 ~ 65535)");
|
||||
}
|
||||
|
||||
this.code = code;
|
||||
this.name = checkNotNull(name, "name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error code for this {@link DnsResponseCode}.
|
||||
*/
|
||||
public int code() {
|
||||
return errorCode;
|
||||
public int intValue() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DnsResponseCode o) {
|
||||
return code() - o.code();
|
||||
return intValue() - o.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return code();
|
||||
return intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality of {@link DnsResponseCode} only depends on {@link #code()}.
|
||||
* Equality of {@link DnsResponseCode} only depends on {@link #intValue()}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
@ -175,7 +199,7 @@ public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
||||
return false;
|
||||
}
|
||||
|
||||
return code() == ((DnsResponseCode) o).code();
|
||||
return intValue() == ((DnsResponseCode) o).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,9 +207,10 @@ public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (message == null) {
|
||||
return "DnsResponseCode(" + errorCode + ')';
|
||||
String text = this.text;
|
||||
if (text == null) {
|
||||
this.text = text = name + '(' + intValue() + ')';
|
||||
}
|
||||
return "DnsResponseCode(" + errorCode + ", " + message + ')';
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DnsResponseDecoder accepts {@link io.netty.channel.socket.DatagramPacket} and encodes to
|
||||
* {@link DnsResponse}. This class also contains methods for decoding parts of
|
||||
* DnsResponses such as questions and resource records.
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class DnsResponseDecoder extends MessageToMessageDecoder<DatagramPacket> {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
|
||||
ByteBuf buf = packet.content();
|
||||
|
||||
int id = buf.readUnsignedShort();
|
||||
|
||||
DnsResponse response = new DnsResponse(id, packet.sender());
|
||||
DnsResponseHeader header = response.header();
|
||||
int flags = buf.readUnsignedShort();
|
||||
header.setType(flags >> 15);
|
||||
header.setOpcode(flags >> 11 & 0xf);
|
||||
header.setRecursionDesired((flags >> 8 & 1) == 1);
|
||||
header.setAuthoritativeAnswer((flags >> 10 & 1) == 1);
|
||||
header.setTruncated((flags >> 9 & 1) == 1);
|
||||
header.setRecursionAvailable((flags >> 7 & 1) == 1);
|
||||
header.setZ(flags >> 4 & 0x7);
|
||||
header.setResponseCode(DnsResponseCode.valueOf(flags & 0xf));
|
||||
|
||||
int questions = buf.readUnsignedShort();
|
||||
int answers = buf.readUnsignedShort();
|
||||
int authorities = buf.readUnsignedShort();
|
||||
int additionals = buf.readUnsignedShort();
|
||||
|
||||
for (int i = 0; i < questions; i++) {
|
||||
response.addQuestion(decodeQuestion(buf));
|
||||
}
|
||||
if (header.responseCode() != DnsResponseCode.NOERROR) {
|
||||
// response code for error
|
||||
out.add(response);
|
||||
return;
|
||||
}
|
||||
boolean release = true;
|
||||
try {
|
||||
for (int i = 0; i < answers; i++) {
|
||||
response.addAnswer(decodeResource(buf));
|
||||
}
|
||||
for (int i = 0; i < authorities; i++) {
|
||||
response.addAuthorityResource(decodeResource(buf));
|
||||
}
|
||||
for (int i = 0; i < additionals; i++) {
|
||||
response.addAdditionalResource(decodeResource(buf));
|
||||
}
|
||||
out.add(response);
|
||||
release = false;
|
||||
} finally {
|
||||
if (release) {
|
||||
// We need to release te DnsResources in case of an Exception as we called retain() on the buffer.
|
||||
releaseDnsResources(response.answers());
|
||||
releaseDnsResources(response.authorityResources());
|
||||
releaseDnsResources(response.additionalResources());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void releaseDnsResources(List<DnsResource> resources) {
|
||||
int size = resources.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
DnsResource resource = resources.get(i);
|
||||
resource.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a domain name given a buffer containing a DNS packet. If the
|
||||
* name contains a pointer, the position of the buffer will be set to
|
||||
* directly after the pointer's index after the name has been read.
|
||||
*
|
||||
* @param buf
|
||||
* the byte buffer containing the DNS packet
|
||||
* @return the domain name for an entry
|
||||
*/
|
||||
private static String readName(ByteBuf buf) {
|
||||
int position = -1;
|
||||
int checked = 0;
|
||||
int length = buf.writerIndex();
|
||||
StringBuilder name = new StringBuilder();
|
||||
for (int len = buf.readUnsignedByte(); buf.isReadable() && len != 0; len = buf.readUnsignedByte()) {
|
||||
boolean pointer = (len & 0xc0) == 0xc0;
|
||||
if (pointer) {
|
||||
if (position == -1) {
|
||||
position = buf.readerIndex() + 1;
|
||||
}
|
||||
buf.readerIndex((len & 0x3f) << 8 | buf.readUnsignedByte());
|
||||
// check for loops
|
||||
checked += 2;
|
||||
if (checked >= length) {
|
||||
throw new CorruptedFrameException("name contains a loop.");
|
||||
}
|
||||
} else {
|
||||
name.append(buf.toString(buf.readerIndex(), len, CharsetUtil.UTF_8)).append('.');
|
||||
buf.skipBytes(len);
|
||||
}
|
||||
}
|
||||
if (position != -1) {
|
||||
buf.readerIndex(position);
|
||||
}
|
||||
if (name.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return name.substring(0, name.length() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a question, given a DNS packet in a byte buffer.
|
||||
*
|
||||
* @param buf
|
||||
* the byte buffer containing the DNS packet
|
||||
* @return a decoded {@link DnsQuestion}
|
||||
*/
|
||||
private static DnsQuestion decodeQuestion(ByteBuf buf) {
|
||||
String name = readName(buf);
|
||||
DnsType type = DnsType.valueOf(buf.readUnsignedShort());
|
||||
DnsClass qClass = DnsClass.valueOf(buf.readUnsignedShort());
|
||||
return new DnsQuestion(name, type, qClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a resource record, given a DNS packet in a byte buffer.
|
||||
*
|
||||
* @param buf
|
||||
* the byte buffer containing the DNS packet
|
||||
* @return a {@link DnsResource} record containing response data
|
||||
*/
|
||||
private static DnsResource decodeResource(ByteBuf buf) {
|
||||
String name = readName(buf);
|
||||
DnsType type = DnsType.valueOf(buf.readUnsignedShort());
|
||||
DnsClass aClass = DnsClass.valueOf(buf.readUnsignedShort());
|
||||
long ttl = buf.readUnsignedInt();
|
||||
int len = buf.readUnsignedShort();
|
||||
|
||||
int readerIndex = buf.readerIndex();
|
||||
ByteBuf payload = buf.duplicate().setIndex(readerIndex, readerIndex + len).retain();
|
||||
buf.readerIndex(readerIndex + len);
|
||||
return new DnsResource(name, type, aClass, ttl, payload);
|
||||
}
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 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;
|
||||
|
||||
/**
|
||||
* The DNS response header class which is used when receiving data from a DNS
|
||||
* server. Contains information contained in a DNS response header, such as
|
||||
* recursion availability, and response codes.
|
||||
*/
|
||||
public final class DnsResponseHeader extends DnsHeader {
|
||||
|
||||
private boolean authoritativeAnswer;
|
||||
private boolean truncated;
|
||||
private boolean recursionAvailable;
|
||||
|
||||
private DnsResponseCode responseCode;
|
||||
|
||||
/**
|
||||
* Constructor for a DNS packet response header. The id is received by
|
||||
* reading a {@link DnsQuery} and is sent back to the client.
|
||||
*
|
||||
* @param parent
|
||||
* the {@link DnsMessage} this header belongs to
|
||||
* @param id
|
||||
* a 2 bit unsigned identification number received from client
|
||||
*/
|
||||
public DnsResponseHeader(DnsMessage parent, int id) {
|
||||
super(parent);
|
||||
setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if responding server is authoritative for the domain
|
||||
* name in the query message.
|
||||
*/
|
||||
public boolean isAuthoritativeAnswer() {
|
||||
return authoritativeAnswer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if response has been truncated, usually if it is
|
||||
* over 512 bytes.
|
||||
*/
|
||||
public boolean isTruncated() {
|
||||
return truncated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if DNS server can handle recursive queries.
|
||||
*/
|
||||
public boolean isRecursionAvailable() {
|
||||
return recursionAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 4 bit return code.
|
||||
*/
|
||||
public DnsResponseCode responseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DnsMessage} type. This will always return
|
||||
* {@code TYPE_RESPONSE}.
|
||||
*/
|
||||
@Override
|
||||
public int type() {
|
||||
return TYPE_RESPONSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if responding server is authoritative for the domain
|
||||
* name in the query message.
|
||||
*
|
||||
* @param authoritativeAnswer
|
||||
* flag for authoritative answer
|
||||
*/
|
||||
public DnsResponseHeader setAuthoritativeAnswer(boolean authoritativeAnswer) {
|
||||
this.authoritativeAnswer = authoritativeAnswer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if response has been truncated (usually happens for
|
||||
* responses over 512 bytes).
|
||||
*
|
||||
* @param truncated
|
||||
* flag for truncation
|
||||
*/
|
||||
public DnsResponseHeader setTruncated(boolean truncated) {
|
||||
this.truncated = truncated;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if DNS server can handle recursive queries.
|
||||
*
|
||||
* @param recursionAvailable
|
||||
* flag for recursion availability
|
||||
*/
|
||||
public DnsResponseHeader setRecursionAvailable(boolean recursionAvailable) {
|
||||
this.recursionAvailable = recursionAvailable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response code for this message.
|
||||
*
|
||||
* @param responseCode
|
||||
* the response code
|
||||
*/
|
||||
public DnsResponseHeader setResponseCode(DnsResponseCode responseCode) {
|
||||
this.responseCode = responseCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link DnsHeader} type. Must be {@code TYPE_RESPONSE}.
|
||||
*
|
||||
* @param type
|
||||
* message type
|
||||
* @return the header to allow method chaining
|
||||
*/
|
||||
@Override
|
||||
public DnsResponseHeader setType(int type) {
|
||||
if (type != TYPE_RESPONSE) {
|
||||
throw new IllegalArgumentException("type cannot be anything but TYPE_RESPONSE (1) for a response header.");
|
||||
}
|
||||
super.setType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponseHeader setId(int id) {
|
||||
super.setId(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsHeader setRecursionDesired(boolean recursionDesired) {
|
||||
return super.setRecursionDesired(recursionDesired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponseHeader setOpcode(int opcode) {
|
||||
super.setOpcode(opcode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DnsResponseHeader setZ(int z) {
|
||||
super.setZ(z);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents a section of a {@link DnsMessage}.
|
||||
*/
|
||||
public enum DnsSection {
|
||||
/**
|
||||
* The section that contains {@link DnsQuestion}s.
|
||||
*/
|
||||
QUESTION,
|
||||
/**
|
||||
* The section that contains the answer {@link DnsRecord}s.
|
||||
*/
|
||||
ANSWER,
|
||||
/**
|
||||
* The section that contains the authority {@link DnsRecord}s.
|
||||
*/
|
||||
AUTHORITY,
|
||||
/**
|
||||
* The section that contains the additional {@link DnsRecord}s.
|
||||
*/
|
||||
ADDITIONAL
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 The Netty Project
|
||||
* 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
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* DNS codec information for writing to and reading from a DNS server.
|
||||
* Includes decoders and classes for representing messages and resources.
|
||||
* DNS codec.
|
||||
*/
|
||||
package io.netty.handler.codec.dns;
|
||||
|
@ -25,29 +25,40 @@ import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class DnsQueryTest {
|
||||
|
||||
@Test
|
||||
public void writeQueryTest() throws Exception {
|
||||
InetSocketAddress addr = new InetSocketAddress(0);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DnsQueryEncoder());
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsQueryEncoder());
|
||||
List<DnsQuery> queries = new ArrayList<DnsQuery>(5);
|
||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("1.0.0.127.in-addr.arpa", DnsType.PTR)));
|
||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("www.example.com", DnsType.A)));
|
||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.AAAA)));
|
||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.MX)));
|
||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.CNAME)));
|
||||
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||
DnsSection.QUESTION,
|
||||
new DefaultDnsQuestion("1.0.0.127.in-addr.arpa", DnsRecordType.PTR)));
|
||||
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||
DnsSection.QUESTION,
|
||||
new DefaultDnsQuestion("www.example.com", DnsRecordType.A)));
|
||||
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||
DnsSection.QUESTION,
|
||||
new DefaultDnsQuestion("example.com", DnsRecordType.AAAA)));
|
||||
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||
DnsSection.QUESTION,
|
||||
new DefaultDnsQuestion("example.com", DnsRecordType.MX)));
|
||||
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||
DnsSection.QUESTION,
|
||||
new DefaultDnsQuestion("example.com", DnsRecordType.CNAME)));
|
||||
|
||||
for (DnsQuery query: queries) {
|
||||
Assert.assertEquals("Invalid question count, expected 1.", 1, query.header().questionCount());
|
||||
Assert.assertEquals("Invalid answer count, expected 0.", 0, query.header().answerCount());
|
||||
Assert.assertEquals("Invalid authority resource record count, expected 0.", 0, query.header()
|
||||
.authorityResourceCount());
|
||||
Assert.assertEquals("Invalid additional resource record count, expected 0.", 0, query.header()
|
||||
.additionalResourceCount());
|
||||
Assert.assertEquals("Invalid type, should be TYPE_QUERY (0)", DnsHeader.TYPE_QUERY, query.header()
|
||||
.type());
|
||||
assertThat(query.count(DnsSection.QUESTION), is(1));
|
||||
assertThat(query.count(DnsSection.ANSWER), is(0));
|
||||
assertThat(query.count(DnsSection.AUTHORITY), is(0));
|
||||
assertThat(query.count(DnsSection.ADDITIONAL), is(0));
|
||||
|
||||
embedder.writeOutbound(query);
|
||||
|
||||
DatagramPacket packet = embedder.readOutbound();
|
||||
Assert.assertTrue(packet.content().isReadable());
|
||||
packet.release();
|
||||
|
@ -25,13 +25,13 @@ import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DnsClassTest {
|
||||
public class DnsRecordTypeTest {
|
||||
|
||||
private static List<DnsClass> allTypes() throws Exception {
|
||||
List<DnsClass> result = new ArrayList<DnsClass>();
|
||||
for (Field field : DnsClass.class.getDeclaredFields()) {
|
||||
if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsClass.class) {
|
||||
result.add((DnsClass) field.get(null));
|
||||
private static List<DnsRecordType> allTypes() throws Exception {
|
||||
List<DnsRecordType> result = new ArrayList<DnsRecordType>();
|
||||
for (Field field : DnsRecordType.class.getFields()) {
|
||||
if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsRecordType.class) {
|
||||
result.add((DnsRecordType) field.get(null));
|
||||
}
|
||||
}
|
||||
assertFalse(result.isEmpty());
|
||||
@ -41,26 +41,26 @@ public class DnsClassTest {
|
||||
@Test
|
||||
public void testSanity() throws Exception {
|
||||
assertEquals("More than one type has the same int value",
|
||||
allTypes().size(), new HashSet<DnsClass>(allTypes()).size());
|
||||
allTypes().size(), new HashSet<DnsRecordType>(allTypes()).size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hashCode method, of class DnsClass.
|
||||
* Test of hashCode method, of class DnsRecordType.
|
||||
*/
|
||||
@Test
|
||||
public void testHashCode() throws Exception {
|
||||
for (DnsClass t : allTypes()) {
|
||||
for (DnsRecordType t : allTypes()) {
|
||||
assertEquals(t.intValue(), t.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of equals method, of class DnsClass.
|
||||
* Test of equals method, of class DnsRecordType.
|
||||
*/
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
for (DnsClass t1 : allTypes()) {
|
||||
for (DnsClass t2 : allTypes()) {
|
||||
for (DnsRecordType t1 : allTypes()) {
|
||||
for (DnsRecordType t2 : allTypes()) {
|
||||
if (t1 != t2) {
|
||||
assertNotEquals(t1, t2);
|
||||
}
|
||||
@ -69,15 +69,15 @@ public class DnsClassTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of find method, of class DnsClass.
|
||||
* Test of find method, of class DnsRecordType.
|
||||
*/
|
||||
@Test
|
||||
public void testFind() throws Exception {
|
||||
for (DnsClass t : allTypes()) {
|
||||
DnsClass found = DnsClass.valueOf(t.intValue());
|
||||
for (DnsRecordType t : allTypes()) {
|
||||
DnsRecordType found = DnsRecordType.valueOf(t.intValue());
|
||||
assertSame(t, found);
|
||||
found = DnsClass.valueOf(t.toString());
|
||||
assertSame(t.toString(), t, found);
|
||||
found = DnsRecordType.valueOf(t.name());
|
||||
assertSame(t.name(), t, found);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,16 +17,19 @@ package io.netty.handler.codec.dns;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DnsResponseTest {
|
||||
|
||||
private static final byte[][] packets = {
|
||||
@ -69,25 +72,23 @@ public class DnsResponseTest {
|
||||
|
||||
@Test
|
||||
public void readResponseTest() throws Exception {
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DnsResponseDecoder());
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder());
|
||||
for (byte[] p: packets) {
|
||||
ByteBuf packet = embedder.alloc().buffer(512).writeBytes(p);
|
||||
embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)));
|
||||
DnsResponse decoded = embedder.readInbound();
|
||||
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = embedder.readInbound();
|
||||
assertThat(envelope, is(instanceOf(DatagramDnsResponse.class)));
|
||||
DnsResponse response = envelope.content();
|
||||
assertThat(response, is(sameInstance((Object) envelope)));
|
||||
|
||||
ByteBuf raw = Unpooled.wrappedBuffer(p);
|
||||
Assert.assertEquals("Invalid id, expected: " + raw.getUnsignedShort(0) + ", actual: "
|
||||
+ decoded.header().id(), raw.getUnsignedShort(0), decoded.header().id());
|
||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(4) + ", actual: "
|
||||
+ decoded.questions().size(), raw.getUnsignedShort(4), decoded.questions().size());
|
||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(6) + ", actual: "
|
||||
+ decoded.answers().size(), raw.getUnsignedShort(6), decoded.answers().size());
|
||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(8) + ", actual: "
|
||||
+ decoded.authorityResources().size(), raw.getUnsignedShort(8), decoded.authorityResources()
|
||||
.size());
|
||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(10) + ", actual: "
|
||||
+ decoded.additionalResources().size(), raw.getUnsignedShort(10),
|
||||
decoded.additionalResources().size());
|
||||
decoded.release();
|
||||
assertThat(response.id(), is(raw.getUnsignedShort(0)));
|
||||
assertThat(response.count(DnsSection.QUESTION), is(raw.getUnsignedShort(4)));
|
||||
assertThat(response.count(DnsSection.ANSWER), is(raw.getUnsignedShort(6)));
|
||||
assertThat(response.count(DnsSection.AUTHORITY), is(raw.getUnsignedShort(8)));
|
||||
assertThat(response.count(DnsSection.ADDITIONAL), is(raw.getUnsignedShort(10)));
|
||||
|
||||
envelope.release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +97,7 @@ public class DnsResponseTest {
|
||||
|
||||
@Test
|
||||
public void readMalormedResponseTest() throws Exception {
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DnsResponseDecoder());
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder());
|
||||
ByteBuf packet = embedder.alloc().buffer(512).writeBytes(malformedLoopPacket);
|
||||
exception.expect(CorruptedFrameException.class);
|
||||
embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)));
|
||||
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 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 org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DnsTypeTest {
|
||||
|
||||
private static List<DnsType> allTypes() throws Exception {
|
||||
List<DnsType> result = new ArrayList<DnsType>();
|
||||
for (Field field : DnsType.class.getFields()) {
|
||||
if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsType.class) {
|
||||
result.add((DnsType) field.get(null));
|
||||
}
|
||||
}
|
||||
assertFalse(result.isEmpty());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanity() throws Exception {
|
||||
assertEquals("More than one type has the same int value",
|
||||
allTypes().size(), new HashSet<DnsType>(allTypes()).size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hashCode method, of class DnsType.
|
||||
*/
|
||||
@Test
|
||||
public void testHashCode() throws Exception {
|
||||
for (DnsType t : allTypes()) {
|
||||
assertEquals(t.intValue(), t.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of equals method, of class DnsType.
|
||||
*/
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
for (DnsType t1 : allTypes()) {
|
||||
for (DnsType t2 : allTypes()) {
|
||||
if (t1 != t2) {
|
||||
assertNotEquals(t1, t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of find method, of class DnsType.
|
||||
*/
|
||||
@Test
|
||||
public void testFind() throws Exception {
|
||||
for (DnsType t : allTypes()) {
|
||||
DnsType found = DnsType.valueOf(t.intValue());
|
||||
assertSame(t, found);
|
||||
found = DnsType.valueOf(t.toString());
|
||||
assertSame(t.toString(), t, found);
|
||||
}
|
||||
}
|
||||
}
|
@ -28,15 +28,19 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
*/
|
||||
public final class StringUtil {
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
public static final String NEWLINE;
|
||||
|
||||
public static final char DOUBLE_QUOTE = '\"';
|
||||
public static final char COMMA = ',';
|
||||
public static final char LINE_FEED = '\n';
|
||||
public static final char CARRIAGE_RETURN = '\r';
|
||||
public static final String EMPTY_STRING = "";
|
||||
public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = (int) 'a' - (int) 'A';
|
||||
public static final char TAB = '\t';
|
||||
|
||||
public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = 'a' - 'A';
|
||||
private static final String[] BYTE2HEX_PAD = new String[256];
|
||||
private static final String[] BYTE2HEX_NOPAD = new String[256];
|
||||
|
||||
/**
|
||||
* 2 - Quote character at beginning and end.
|
||||
* 5 - Extra allowance for anticipated escape characters that may be added.
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.resolver.dns;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.ChannelFactory;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
@ -27,13 +28,14 @@ import io.netty.channel.FixedRecvByteBufAllocator;
|
||||
import io.netty.channel.ReflectiveChannelFactory;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.InternetProtocolFamily;
|
||||
import io.netty.handler.codec.dns.DnsClass;
|
||||
import io.netty.handler.codec.dns.DnsQueryEncoder;
|
||||
import io.netty.handler.codec.dns.DatagramDnsQueryEncoder;
|
||||
import io.netty.handler.codec.dns.DatagramDnsResponse;
|
||||
import io.netty.handler.codec.dns.DatagramDnsResponseDecoder;
|
||||
import io.netty.handler.codec.dns.DnsSection;
|
||||
import io.netty.handler.codec.dns.DnsQuestion;
|
||||
import io.netty.handler.codec.dns.DnsResource;
|
||||
import io.netty.handler.codec.dns.DnsRecord;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.handler.codec.dns.DnsResponseCode;
|
||||
import io.netty.handler.codec.dns.DnsResponseDecoder;
|
||||
import io.netty.resolver.NameResolver;
|
||||
import io.netty.resolver.SimpleNameResolver;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
@ -83,8 +85,8 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
}
|
||||
}
|
||||
|
||||
private static final DnsResponseDecoder DECODER = new DnsResponseDecoder();
|
||||
private static final DnsQueryEncoder ENCODER = new DnsQueryEncoder();
|
||||
private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder();
|
||||
private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder();
|
||||
|
||||
final Iterable<InetSocketAddress> nameServerAddresses;
|
||||
final ChannelFuture bindFuture;
|
||||
@ -117,7 +119,6 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
private volatile int maxQueriesPerResolve = 8;
|
||||
|
||||
private volatile int maxPayloadSize;
|
||||
private volatile DnsClass maxPayloadSizeClass; // EDNS uses the CLASS field as the payload size field.
|
||||
|
||||
/**
|
||||
* Creates a new DNS-based name resolver that communicates with a single DNS server.
|
||||
@ -580,16 +581,11 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
}
|
||||
|
||||
this.maxPayloadSize = maxPayloadSize;
|
||||
maxPayloadSizeClass = DnsClass.valueOf(maxPayloadSize);
|
||||
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
DnsClass maxPayloadSizeClass() {
|
||||
return maxPayloadSizeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the DNS resource records cached by this resolver.
|
||||
*
|
||||
@ -662,21 +658,23 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
/**
|
||||
* Sends a DNS query with the specified question.
|
||||
*/
|
||||
public Future<DnsResponse> query(DnsQuestion question) {
|
||||
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(DnsQuestion question) {
|
||||
return query(nameServerAddresses, question);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DNS query with the specified question.
|
||||
*/
|
||||
public Future<DnsResponse> query(DnsQuestion question, Promise<DnsResponse> promise) {
|
||||
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||
DnsQuestion question, Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||
return query(nameServerAddresses, question, promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DNS query with the specified question using the specified name server list.
|
||||
*/
|
||||
public Future<DnsResponse> query(Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question) {
|
||||
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question) {
|
||||
if (nameServerAddresses == null) {
|
||||
throw new NullPointerException("nameServerAddresses");
|
||||
}
|
||||
@ -693,15 +691,18 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
return eventLoop.newFailedFuture(cachedResult.cause);
|
||||
}
|
||||
} else {
|
||||
return query0(nameServerAddresses, question, eventLoop.<DnsResponse>newPromise());
|
||||
return query0(
|
||||
nameServerAddresses, question,
|
||||
eventLoop.<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>>newPromise());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DNS query with the specified question using the specified name server list.
|
||||
*/
|
||||
public Future<DnsResponse> query(
|
||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question, Promise<DnsResponse> promise) {
|
||||
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question,
|
||||
Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||
|
||||
if (nameServerAddresses == null) {
|
||||
throw new NullPointerException("nameServerAddresses");
|
||||
@ -716,23 +717,25 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
final DnsCacheEntry cachedResult = queryCache.get(question);
|
||||
if (cachedResult != null) {
|
||||
if (cachedResult.response != null) {
|
||||
return promise.setSuccess(cachedResult.response.retain());
|
||||
return cast(promise).setSuccess(cachedResult.response.retain());
|
||||
} else {
|
||||
return promise.setFailure(cachedResult.cause);
|
||||
return cast(promise).setFailure(cachedResult.cause);
|
||||
}
|
||||
} else {
|
||||
return query0(nameServerAddresses, question, promise);
|
||||
}
|
||||
}
|
||||
|
||||
private Future<DnsResponse> query0(
|
||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question, Promise<DnsResponse> promise) {
|
||||
private Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(
|
||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question,
|
||||
Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||
|
||||
final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> castPromise = cast(promise);
|
||||
try {
|
||||
new DnsQueryContext(this, nameServerAddresses, question, promise).query();
|
||||
return promise;
|
||||
new DnsQueryContext(this, nameServerAddresses, question, castPromise).query();
|
||||
return castPromise;
|
||||
} catch (Exception e) {
|
||||
return promise.setFailure(e);
|
||||
return castPromise.setFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -762,12 +765,22 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> cast(Promise<?> promise) {
|
||||
return (Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>>) promise;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static AddressedEnvelope<DnsResponse, InetSocketAddress> cast(AddressedEnvelope<?, ?> envelope) {
|
||||
return (AddressedEnvelope<DnsResponse, InetSocketAddress>) envelope;
|
||||
}
|
||||
|
||||
private final class DnsResponseHandler extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
try {
|
||||
final DnsResponse res = (DnsResponse) msg;
|
||||
final int queryId = res.header().id();
|
||||
final DatagramDnsResponse res = (DatagramDnsResponse) msg;
|
||||
final int queryId = res.id();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} RECEIVED: [{}: {}], {}", ch, queryId, res.sender(), res);
|
||||
@ -782,14 +795,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<DnsQuestion> questions = res.questions();
|
||||
if (questions.size() != 1) {
|
||||
if (res.count(DnsSection.QUESTION) != 1) {
|
||||
logger.warn("Received a DNS response with invalid number of questions: {}", res);
|
||||
return;
|
||||
}
|
||||
|
||||
final DnsQuestion q = qCtx.question();
|
||||
if (!q.equals(questions.get(0))) {
|
||||
if (!q.equals(res.recordAt(DnsSection.QUESTION))) {
|
||||
logger.warn("Received a mismatching DNS response: {}", res);
|
||||
return;
|
||||
}
|
||||
@ -800,26 +812,26 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
timeoutFuture.cancel(false);
|
||||
}
|
||||
|
||||
if (res.header().responseCode() == DnsResponseCode.NOERROR) {
|
||||
if (res.code() == DnsResponseCode.NOERROR) {
|
||||
cache(q, res);
|
||||
promises.set(queryId, null);
|
||||
|
||||
Promise<DnsResponse> qPromise = qCtx.promise();
|
||||
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> qPromise = qCtx.promise();
|
||||
if (qPromise.setUncancellable()) {
|
||||
qPromise.setSuccess(res.retain());
|
||||
qPromise.setSuccess(cast(res.retain()));
|
||||
}
|
||||
} else {
|
||||
qCtx.retry(res.sender(),
|
||||
"response code: " + res.header().responseCode() +
|
||||
" with " + res.answers().size() + " answer(s) and " +
|
||||
res.authorityResources().size() + " authority resource(s)");
|
||||
"response code: " + res.code() +
|
||||
" with " + res.count(DnsSection.ANSWER) + " answer(s) and " +
|
||||
res.count(DnsSection.AUTHORITY) + " authority resource(s)");
|
||||
}
|
||||
} finally {
|
||||
ReferenceCountUtil.safeRelease(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void cache(DnsQuestion question, DnsResponse res) {
|
||||
private void cache(DnsQuestion question, AddressedEnvelope<? extends DnsResponse, InetSocketAddress> res) {
|
||||
final int maxTtl = maxTtl();
|
||||
if (maxTtl == 0) {
|
||||
return;
|
||||
@ -827,8 +839,11 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
|
||||
long ttl = Long.MAX_VALUE;
|
||||
// Find the smallest TTL value returned by the server.
|
||||
for (DnsResource r: res.answers()) {
|
||||
long rTtl = r.timeToLive();
|
||||
final DnsResponse resc = res.content();
|
||||
final int answerCount = resc.count(DnsSection.ANSWER);
|
||||
for (int i = 0; i < answerCount; i ++) {
|
||||
final DnsRecord r = resc.recordAt(DnsSection.ANSWER, i);
|
||||
final long rTtl = r.timeToLive();
|
||||
if (ttl > rTtl) {
|
||||
ttl = rTtl;
|
||||
}
|
||||
@ -847,12 +862,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
}
|
||||
|
||||
static final class DnsCacheEntry {
|
||||
final DnsResponse response;
|
||||
final AddressedEnvelope<DnsResponse, InetSocketAddress> response;
|
||||
final Throwable cause;
|
||||
volatile ScheduledFuture<?> expirationFuture;
|
||||
|
||||
DnsCacheEntry(DnsResponse response) {
|
||||
this.response = response.retain();
|
||||
@SuppressWarnings("unchecked")
|
||||
DnsCacheEntry(AddressedEnvelope<? extends DnsResponse, InetSocketAddress> response) {
|
||||
this.response = (AddressedEnvelope<DnsResponse, InetSocketAddress>) response.retain();
|
||||
cause = null;
|
||||
}
|
||||
|
||||
@ -862,7 +878,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
||||
}
|
||||
|
||||
void release() {
|
||||
DnsResponse response = this.response;
|
||||
AddressedEnvelope<DnsResponse, InetSocketAddress> response = this.response;
|
||||
if (response != null) {
|
||||
ReferenceCountUtil.safeRelease(response);
|
||||
}
|
||||
|
@ -17,13 +17,17 @@
|
||||
package io.netty.resolver.dns;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.socket.InternetProtocolFamily;
|
||||
import io.netty.handler.codec.dns.DnsClass;
|
||||
import io.netty.handler.codec.dns.DefaultDnsQuestion;
|
||||
import io.netty.handler.codec.dns.DefaultDnsRecordDecoder;
|
||||
import io.netty.handler.codec.dns.DnsSection;
|
||||
import io.netty.handler.codec.dns.DnsQuestion;
|
||||
import io.netty.handler.codec.dns.DnsResource;
|
||||
import io.netty.handler.codec.dns.DnsRawRecord;
|
||||
import io.netty.handler.codec.dns.DnsRecord;
|
||||
import io.netty.handler.codec.dns.DnsRecordType;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.handler.codec.dns.DnsResponseDecoder;
|
||||
import io.netty.handler.codec.dns.DnsType;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
@ -51,9 +55,10 @@ final class DnsNameResolverContext {
|
||||
private static final int INADDRSZ4 = 4;
|
||||
private static final int INADDRSZ6 = 16;
|
||||
|
||||
private static final FutureListener<DnsResponse> RELEASE_RESPONSE = new FutureListener<DnsResponse>() {
|
||||
private static final FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>> RELEASE_RESPONSE =
|
||||
new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<DnsResponse> future) {
|
||||
public void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) {
|
||||
if (future.isSuccess()) {
|
||||
future.getNow().release();
|
||||
}
|
||||
@ -67,8 +72,10 @@ final class DnsNameResolverContext {
|
||||
private final int maxAllowedQueries;
|
||||
private final InternetProtocolFamily[] resolveAddressTypes;
|
||||
|
||||
private final Set<Future<DnsResponse>> queriesInProgress =
|
||||
Collections.newSetFromMap(new IdentityHashMap<Future<DnsResponse>, Boolean>());
|
||||
private final Set<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> queriesInProgress =
|
||||
Collections.newSetFromMap(
|
||||
new IdentityHashMap<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>, Boolean>());
|
||||
|
||||
private List<InetAddress> resolvedAddresses;
|
||||
private StringBuilder trace;
|
||||
private int allowedQueries;
|
||||
@ -87,19 +94,19 @@ final class DnsNameResolverContext {
|
||||
|
||||
void resolve() {
|
||||
for (InternetProtocolFamily f: resolveAddressTypes) {
|
||||
final DnsType type;
|
||||
final DnsRecordType type;
|
||||
switch (f) {
|
||||
case IPv4:
|
||||
type = DnsType.A;
|
||||
type = DnsRecordType.A;
|
||||
break;
|
||||
case IPv6:
|
||||
type = DnsType.AAAA;
|
||||
type = DnsRecordType.AAAA;
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
query(parent.nameServerAddresses, new DnsQuestion(hostname, type));
|
||||
query(parent.nameServerAddresses, new DefaultDnsQuestion(hostname, type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,12 +117,12 @@ final class DnsNameResolverContext {
|
||||
|
||||
allowedQueries --;
|
||||
|
||||
final Future<DnsResponse> f = parent.query(nameServerAddresses, question);
|
||||
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = parent.query(nameServerAddresses, question);
|
||||
queriesInProgress.add(f);
|
||||
|
||||
f.addListener(new FutureListener<DnsResponse>() {
|
||||
f.addListener(new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<DnsResponse> future) throws Exception {
|
||||
public void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) {
|
||||
queriesInProgress.remove(future);
|
||||
|
||||
if (promise.isDone()) {
|
||||
@ -135,12 +142,12 @@ final class DnsNameResolverContext {
|
||||
});
|
||||
}
|
||||
|
||||
void onResponse(final DnsQuestion question, final DnsResponse response) {
|
||||
final DnsType type = question.type();
|
||||
void onResponse(final DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> response) {
|
||||
final DnsRecordType type = question.type();
|
||||
try {
|
||||
if (type == DnsType.A || type == DnsType.AAAA) {
|
||||
if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
|
||||
onResponseAorAAAA(type, question, response);
|
||||
} else if (type == DnsType.CNAME) {
|
||||
} else if (type == DnsRecordType.CNAME) {
|
||||
onResponseCNAME(question, response);
|
||||
}
|
||||
} finally {
|
||||
@ -148,14 +155,19 @@ final class DnsNameResolverContext {
|
||||
}
|
||||
}
|
||||
|
||||
private void onResponseAorAAAA(DnsType qType, DnsQuestion question, DnsResponse response) {
|
||||
private void onResponseAorAAAA(
|
||||
DnsRecordType qType, DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) {
|
||||
|
||||
// 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 int answerCount = response.count(DnsSection.ANSWER);
|
||||
|
||||
boolean found = false;
|
||||
for (DnsResource r: response.answers()) {
|
||||
final DnsType type = r.type();
|
||||
if (type != DnsType.A && type != DnsType.AAAA) {
|
||||
for (int i = 0; i < answerCount; i ++) {
|
||||
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||
final DnsRecordType type = r.type();
|
||||
if (type != DnsRecordType.A && type != DnsRecordType.AAAA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -178,7 +190,11 @@ final class DnsNameResolverContext {
|
||||
}
|
||||
}
|
||||
|
||||
final ByteBuf content = r.content();
|
||||
if (!(r instanceof DnsRawRecord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ByteBuf content = ((ByteBufHolder) r).content();
|
||||
final int contentLen = content.readableBytes();
|
||||
if (contentLen != INADDRSZ4 && contentLen != INADDRSZ6) {
|
||||
continue;
|
||||
@ -204,20 +220,21 @@ final class DnsNameResolverContext {
|
||||
return;
|
||||
}
|
||||
|
||||
addTrace(response.sender(), "no matching " + qType + " record found");
|
||||
addTrace(envelope.sender(), "no matching " + qType + " record found");
|
||||
|
||||
// We aked for A/AAAA but we got only CNAME.
|
||||
if (!cnames.isEmpty()) {
|
||||
onResponseCNAME(question, response, cnames, false);
|
||||
onResponseCNAME(question, envelope, cnames, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void onResponseCNAME(DnsQuestion question, DnsResponse response) {
|
||||
onResponseCNAME(question, response, buildAliasMap(response), true);
|
||||
private void onResponseCNAME(DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) {
|
||||
onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), true);
|
||||
}
|
||||
|
||||
private void onResponseCNAME(
|
||||
DnsQuestion question, DnsResponse response, Map<String, String> cnames, boolean trace) {
|
||||
DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> response,
|
||||
Map<String, String> cnames, boolean trace) {
|
||||
|
||||
// Resolve the host name in the question into the real host name.
|
||||
final String name = question.name().toLowerCase(Locale.US);
|
||||
@ -241,15 +258,22 @@ final class DnsNameResolverContext {
|
||||
}
|
||||
|
||||
private static Map<String, String> buildAliasMap(DnsResponse response) {
|
||||
final int answerCount = response.count(DnsSection.ANSWER);
|
||||
Map<String, String> cnames = null;
|
||||
for (DnsResource r: response.answers()) {
|
||||
final DnsType type = r.type();
|
||||
if (type != DnsType.CNAME) {
|
||||
for (int i = 0; i < answerCount; i ++) {
|
||||
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||
final DnsRecordType type = r.type();
|
||||
if (type != DnsRecordType.CNAME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String content = decodeDomainName(r.content());
|
||||
if (content == null) {
|
||||
if (!(r instanceof DnsRawRecord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ByteBuf recordContent = ((ByteBufHolder) r).content();
|
||||
final String domainName = decodeDomainName(recordContent);
|
||||
if (domainName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -257,7 +281,7 @@ final class DnsNameResolverContext {
|
||||
cnames = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
cnames.put(r.name().toLowerCase(Locale.US), content.toLowerCase(Locale.US));
|
||||
cnames.put(r.name().toLowerCase(Locale.US), domainName.toLowerCase(Locale.US));
|
||||
}
|
||||
|
||||
return cnames != null? cnames : Collections.<String, String>emptyMap();
|
||||
@ -281,7 +305,7 @@ final class DnsNameResolverContext {
|
||||
if (!triedCNAME) {
|
||||
// As the last resort, try to query CNAME, just in case the name server has it.
|
||||
triedCNAME = true;
|
||||
query(parent.nameServerAddresses, new DnsQuestion(hostname, DnsType.CNAME, DnsClass.IN));
|
||||
query(parent.nameServerAddresses, new DefaultDnsQuestion(hostname, DnsRecordType.CNAME));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -319,8 +343,9 @@ final class DnsNameResolverContext {
|
||||
private void finishResolve() {
|
||||
if (!queriesInProgress.isEmpty()) {
|
||||
// If there are queries in progress, we should cancel it because we already finished the resolution.
|
||||
for (Iterator<Future<DnsResponse>> i = queriesInProgress.iterator(); i.hasNext();) {
|
||||
Future<DnsResponse> f = i.next();
|
||||
for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator();
|
||||
i.hasNext();) {
|
||||
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = i.next();
|
||||
i.remove();
|
||||
|
||||
if (!f.cancel(false)) {
|
||||
@ -392,25 +417,32 @@ final class DnsNameResolverContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from {@link DnsResponseDecoder#readName(ByteBuf)}.
|
||||
* Adapted from {@link DefaultDnsRecordDecoder#decodeName(ByteBuf)}.
|
||||
*/
|
||||
static String decodeDomainName(ByteBuf buf) {
|
||||
buf.markReaderIndex();
|
||||
try {
|
||||
int position = -1;
|
||||
int checked = 0;
|
||||
int length = buf.writerIndex();
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
final int end = buf.writerIndex();
|
||||
final StringBuilder name = new StringBuilder(buf.readableBytes() << 1);
|
||||
for (int len = buf.readUnsignedByte(); buf.isReadable() && len != 0; len = buf.readUnsignedByte()) {
|
||||
boolean pointer = (len & 0xc0) == 0xc0;
|
||||
if (pointer) {
|
||||
if (position == -1) {
|
||||
position = buf.readerIndex() + 1;
|
||||
}
|
||||
buf.readerIndex((len & 0x3f) << 8 | buf.readUnsignedByte());
|
||||
|
||||
final int next = (len & 0x3f) << 8 | buf.readUnsignedByte();
|
||||
if (next >= end) {
|
||||
// Should not happen.
|
||||
return null;
|
||||
}
|
||||
buf.readerIndex(next);
|
||||
|
||||
// check for loops
|
||||
checked += 2;
|
||||
if (checked >= length) {
|
||||
if (checked >= end) {
|
||||
// Name contains a loop; give up.
|
||||
return null;
|
||||
}
|
||||
@ -449,8 +481,8 @@ final class DnsNameResolverContext {
|
||||
trace.append(" CNAME ");
|
||||
trace.append(cname);
|
||||
|
||||
query(parent.nameServerAddresses, new DnsQuestion(cname, DnsType.A, DnsClass.IN));
|
||||
query(parent.nameServerAddresses, new DnsQuestion(cname, DnsType.AAAA, DnsClass.IN));
|
||||
query(parent.nameServerAddresses, new DefaultDnsQuestion(cname, DnsRecordType.A));
|
||||
query(parent.nameServerAddresses, new DefaultDnsQuestion(cname, DnsRecordType.AAAA));
|
||||
}
|
||||
|
||||
private void addTrace(InetSocketAddress nameServerAddr, String msg) {
|
||||
|
@ -17,13 +17,17 @@
|
||||
package io.netty.resolver.dns;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.handler.codec.dns.DatagramDnsQuery;
|
||||
import io.netty.handler.codec.dns.DefaultDnsRawRecord;
|
||||
import io.netty.handler.codec.dns.DnsSection;
|
||||
import io.netty.handler.codec.dns.DnsQuery;
|
||||
import io.netty.handler.codec.dns.DnsQuestion;
|
||||
import io.netty.handler.codec.dns.DnsResource;
|
||||
import io.netty.handler.codec.dns.DnsRecord;
|
||||
import io.netty.handler.codec.dns.DnsRecordType;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.handler.codec.dns.DnsType;
|
||||
import io.netty.resolver.dns.DnsNameResolver.DnsCacheEntry;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
@ -43,10 +47,10 @@ final class DnsQueryContext {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class);
|
||||
|
||||
private final DnsNameResolver parent;
|
||||
private final Promise<DnsResponse> promise;
|
||||
private final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise;
|
||||
private final int id;
|
||||
private final DnsQuestion question;
|
||||
private final DnsResource optResource;
|
||||
private final DnsRecord optResource;
|
||||
private final Iterator<InetSocketAddress> nameServerAddresses;
|
||||
|
||||
private final boolean recursionDesired;
|
||||
@ -57,7 +61,7 @@ final class DnsQueryContext {
|
||||
|
||||
DnsQueryContext(DnsNameResolver parent,
|
||||
Iterable<InetSocketAddress> nameServerAddresses,
|
||||
DnsQuestion question, Promise<DnsResponse> promise) {
|
||||
DnsQuestion question, Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) {
|
||||
|
||||
this.parent = parent;
|
||||
this.promise = promise;
|
||||
@ -67,7 +71,8 @@ final class DnsQueryContext {
|
||||
recursionDesired = parent.isRecursionDesired();
|
||||
maxTries = parent.maxTriesPerQuery();
|
||||
remainingTries = maxTries;
|
||||
optResource = new DnsResource("", DnsType.OPT, parent.maxPayloadSizeClass(), 0, Unpooled.EMPTY_BUFFER);
|
||||
optResource = new DefaultDnsRawRecord(
|
||||
StringUtil.EMPTY_STRING, DnsRecordType.OPT, parent.maxPayloadSize(), 0, Unpooled.EMPTY_BUFFER);
|
||||
|
||||
this.nameServerAddresses = nameServerAddresses.iterator();
|
||||
}
|
||||
@ -89,7 +94,7 @@ final class DnsQueryContext {
|
||||
}
|
||||
}
|
||||
|
||||
Promise<DnsResponse> promise() {
|
||||
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise() {
|
||||
return promise;
|
||||
}
|
||||
|
||||
@ -125,10 +130,10 @@ final class DnsQueryContext {
|
||||
remainingTries --;
|
||||
|
||||
final InetSocketAddress nameServerAddr = nameServerAddresses.next();
|
||||
final DnsQuery query = new DnsQuery(id, nameServerAddr);
|
||||
query.addQuestion(question);
|
||||
query.header().setRecursionDesired(recursionDesired);
|
||||
query.addAdditionalResource(optResource);
|
||||
final DatagramDnsQuery query = new DatagramDnsQuery(null, nameServerAddr, id);
|
||||
query.setRecursionDesired(recursionDesired);
|
||||
query.setRecord(DnsSection.QUESTION, question);
|
||||
query.setRecord(DnsSection.ADDITIONAL, optResource);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} WRITE: [{}: {}], {}", parent.ch, id, nameServerAddr, question);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 The Netty Project
|
||||
* 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
|
||||
@ -13,18 +13,21 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.resolver.dns;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.InternetProtocolFamily;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.handler.codec.dns.DnsQuestion;
|
||||
import io.netty.handler.codec.dns.DnsResource;
|
||||
import io.netty.handler.codec.dns.DefaultDnsQuestion;
|
||||
import io.netty.handler.codec.dns.DnsSection;
|
||||
import io.netty.handler.codec.dns.DnsRecord;
|
||||
import io.netty.handler.codec.dns.DnsRecordType;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.handler.codec.dns.DnsResponseCode;
|
||||
import io.netty.handler.codec.dns.DnsType;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
@ -181,7 +184,7 @@ public class DnsNameResolverTest {
|
||||
EXCLUSIONS_RESOLVE_A,
|
||||
"akamaihd.net",
|
||||
"googleusercontent.com",
|
||||
"");
|
||||
StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,7 +239,7 @@ public class DnsNameResolverTest {
|
||||
"people.com.cn",
|
||||
"googleusercontent.com",
|
||||
"blogspot.in",
|
||||
"");
|
||||
StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
||||
@ -368,8 +371,8 @@ public class DnsNameResolverTest {
|
||||
public void testQueryMx() throws Exception {
|
||||
assertThat(resolver.isRecursionDesired(), is(true));
|
||||
|
||||
Map<String, Future<DnsResponse>> futures =
|
||||
new LinkedHashMap<String, Future<DnsResponse>>();
|
||||
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures =
|
||||
new LinkedHashMap<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>>();
|
||||
for (String name: DOMAINS) {
|
||||
if (EXCLUSIONS_QUERY_MX.contains(name)) {
|
||||
continue;
|
||||
@ -378,30 +381,36 @@ public class DnsNameResolverTest {
|
||||
queryMx(futures, name);
|
||||
}
|
||||
|
||||
for (Entry<String, Future<DnsResponse>> e: futures.entrySet()) {
|
||||
for (Entry<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> e: futures.entrySet()) {
|
||||
String hostname = e.getKey();
|
||||
DnsResponse response = e.getValue().sync().getNow();
|
||||
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = e.getValue().sync().getNow();
|
||||
DnsResponse response = envelope.content();
|
||||
|
||||
assertThat(response.header().responseCode(), is(DnsResponseCode.NOERROR));
|
||||
List<DnsResource> mxList = new ArrayList<DnsResource>();
|
||||
for (DnsResource r: response.answers()) {
|
||||
if (r.type() == DnsType.MX) {
|
||||
assertThat(response.code(), is(DnsResponseCode.NOERROR));
|
||||
|
||||
final int answerCount = response.count(DnsSection.ANSWER);
|
||||
final List<DnsRecord> mxList = new ArrayList<DnsRecord>(answerCount);
|
||||
for (int i = 0; i < answerCount; i ++) {
|
||||
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||
if (r.type() == DnsRecordType.MX) {
|
||||
mxList.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(mxList.size(), is(greaterThan(0)));
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (DnsResource r: mxList) {
|
||||
for (DnsRecord r: mxList) {
|
||||
ByteBuf recordContent = ((ByteBufHolder) r).content();
|
||||
|
||||
buf.append(StringUtil.NEWLINE);
|
||||
buf.append('\t');
|
||||
buf.append(r.name());
|
||||
buf.append(' ');
|
||||
buf.append(r.type());
|
||||
buf.append(r.type().name());
|
||||
buf.append(' ');
|
||||
buf.append(r.content().readUnsignedShort());
|
||||
buf.append(recordContent.readUnsignedShort());
|
||||
buf.append(' ');
|
||||
buf.append(DnsNameResolverContext.decodeDomainName(r.content()));
|
||||
buf.append(DnsNameResolverContext.decodeDomainName(recordContent));
|
||||
}
|
||||
|
||||
logger.info("{} has the following MX records:{}", hostname, buf);
|
||||
@ -409,14 +418,17 @@ public class DnsNameResolverTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolve(Map<InetSocketAddress, Future<InetSocketAddress>> futures, String hostname) {
|
||||
private static void resolve(
|
||||
Map<InetSocketAddress, Future<InetSocketAddress>> futures, String hostname) {
|
||||
InetSocketAddress unresolved =
|
||||
InetSocketAddress.createUnresolved(hostname, ThreadLocalRandom.current().nextInt(65536));
|
||||
|
||||
futures.put(unresolved, resolver.resolve(unresolved));
|
||||
}
|
||||
|
||||
private static void queryMx(Map<String, Future<DnsResponse>> futures, String hostname) throws Exception {
|
||||
futures.put(hostname, resolver.query(new DnsQuestion(hostname, DnsType.MX)));
|
||||
private static void queryMx(
|
||||
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures,
|
||||
String hostname) throws Exception {
|
||||
futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX)));
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,10 @@ public class DefaultAddressedEnvelope<M, A extends SocketAddress> implements Add
|
||||
throw new NullPointerException("message");
|
||||
}
|
||||
|
||||
if (recipient == null && sender == null) {
|
||||
throw new NullPointerException("recipient and sender");
|
||||
}
|
||||
|
||||
this.message = message;
|
||||
this.sender = sender;
|
||||
this.recipient = recipient;
|
||||
|
Loading…
Reference in New Issue
Block a user