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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -15,225 +15,142 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
package io.netty.handler.codec.dns;
|
||||||
|
|
||||||
import io.netty.util.AbstractReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The message super-class which contains core information concerning DNS
|
* The superclass which contains core information concerning a {@link DnsQuery} and a {@link DnsResponse}.
|
||||||
* packets, both outgoing and incoming.
|
|
||||||
*/
|
*/
|
||||||
public abstract class DnsMessage extends AbstractReferenceCounted {
|
public interface DnsMessage extends ReferenceCounted {
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the header belonging to this message.
|
* Returns the {@code ID} of this DNS message.
|
||||||
*/
|
*/
|
||||||
public DnsHeader header() {
|
int id();
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the questions in this message.
|
* Sets the {@code ID} of this DNS message.
|
||||||
*/
|
*/
|
||||||
public List<DnsQuestion> questions() {
|
DnsMessage setId(int id);
|
||||||
if (questions == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(questions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the answer resource records in this message.
|
* Returns the {@code opCode} of this DNS message.
|
||||||
*/
|
*/
|
||||||
public List<DnsResource> answers() {
|
DnsOpCode opCode();
|
||||||
if (answers == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(answers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all the authority resource records in this message.
|
* Sets the {@code opCode} of this DNS message.
|
||||||
*/
|
*/
|
||||||
public List<DnsResource> authorityResources() {
|
DnsMessage setOpCode(DnsOpCode opCode);
|
||||||
if (authority == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(authority);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() {
|
boolean isRecursionDesired();
|
||||||
if (additional == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(additional);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @return {@code null} if this message doesn't have any records in the specified {@code section}
|
||||||
* the answer resource record to be added
|
|
||||||
* @return the message to allow method chaining
|
|
||||||
*/
|
*/
|
||||||
public DnsMessage addAnswer(DnsResource answer) {
|
<T extends DnsRecord> T recordAt(DnsSection section);
|
||||||
if (answers == null) {
|
|
||||||
answers = new LinkedList<DnsResource>();
|
|
||||||
}
|
|
||||||
answers.add(answer);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds
|
||||||
* the question to be added
|
|
||||||
* @return the message to allow method chaining
|
|
||||||
*/
|
*/
|
||||||
public DnsMessage addQuestion(DnsQuestion question) {
|
<T extends DnsRecord> T recordAt(DnsSection section, int index);
|
||||||
if (questions == null) {
|
|
||||||
questions = new LinkedList<DnsQuestion>();
|
|
||||||
}
|
|
||||||
questions.add(question);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an authority resource record to this message.
|
* 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},
|
||||||
* @param resource
|
* the specified {@code record} must be a {@link DnsQuestion}.
|
||||||
* the authority resource record to be added
|
|
||||||
* @return the message to allow method chaining
|
|
||||||
*/
|
*/
|
||||||
public DnsMessage addAuthorityResource(DnsResource resource) {
|
DnsMessage setRecord(DnsSection section, DnsRecord record);
|
||||||
if (authority == null) {
|
|
||||||
authority = new LinkedList<DnsResource>();
|
|
||||||
}
|
|
||||||
authority.add(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @return the old record
|
||||||
* the additional resource record to be added
|
* @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds
|
||||||
* @return the message to allow method chaining
|
|
||||||
*/
|
*/
|
||||||
public DnsMessage addAdditionalResource(DnsResource resource) {
|
<T extends DnsRecord> T setRecord(DnsSection section, int index, DnsRecord record);
|
||||||
if (additional == null) {
|
|
||||||
additional = new LinkedList<DnsResource>();
|
/**
|
||||||
}
|
* Adds the specified {@code record} at the end of the specified {@code section} of this DNS message.
|
||||||
additional.add(resource);
|
* When the specified {@code section} is {@link DnsSection#QUESTION}, the specified {@code record}
|
||||||
return this;
|
* 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
|
@Override
|
||||||
protected void deallocate() {
|
DnsMessage touch();
|
||||||
// NOOP
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean release() {
|
DnsMessage touch(Object hint);
|
||||||
release(questions());
|
|
||||||
release(answers());
|
|
||||||
release(additionalResources());
|
|
||||||
release(authorityResources());
|
|
||||||
return super.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void release(List<?> resources) {
|
|
||||||
for (Object resource: resources) {
|
|
||||||
ReferenceCountUtil.release(resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean release(int decrement) {
|
DnsMessage retain();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsMessage touch(Object hint) {
|
DnsMessage retain(int increment);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -15,89 +15,46 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
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
|
* A DNS query message.
|
||||||
* with information answering a DnsQuery's questions.
|
|
||||||
*/
|
*/
|
||||||
public class DnsQuery extends DnsMessage {
|
public interface DnsQuery extends DnsMessage {
|
||||||
|
@Override
|
||||||
private final InetSocketAddress recipient;
|
DnsQuery setId(int id);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery addAnswer(DnsResource answer) {
|
DnsQuery setOpCode(DnsOpCode opCode);
|
||||||
super.addAnswer(answer);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery addQuestion(DnsQuestion question) {
|
DnsQuery setRecursionDesired(boolean recursionDesired);
|
||||||
super.addQuestion(question);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery addAuthorityResource(DnsResource resource) {
|
DnsQuery setZ(int z);
|
||||||
super.addAuthorityResource(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery addAdditionalResource(DnsResource resource) {
|
DnsQuery setRecord(DnsSection section, DnsRecord record);
|
||||||
super.addAdditionalResource(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery touch(Object hint) {
|
DnsQuery addRecord(DnsSection section, DnsRecord record);
|
||||||
super.touch(hint);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery retain() {
|
DnsQuery addRecord(DnsSection section, int index, DnsRecord record);
|
||||||
super.retain();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery retain(int increment) {
|
DnsQuery clear(DnsSection section);
|
||||||
super.retain(increment);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQuery touch() {
|
DnsQuery clear();
|
||||||
super.touch();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsQueryHeader header() {
|
DnsQuery touch();
|
||||||
return (DnsQueryHeader) super.header();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DnsQueryHeader newHeader(int id) {
|
DnsQuery touch(Object hint);
|
||||||
return new DnsQueryHeader(this, id);
|
|
||||||
}
|
@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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -15,58 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
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 DNS question.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
public final class DnsQuestion extends DnsEntry {
|
public interface DnsQuestion extends DnsRecord {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a question with the default class IN (Internet).
|
* An unused property. This method will always return {@code 0}.
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
*/
|
||||||
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
|
@Override
|
||||||
public boolean equals(Object other) {
|
long timeToLive();
|
||||||
if (!(other instanceof DnsQuestion)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return super.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -16,32 +16,33 @@
|
|||||||
package io.netty.handler.codec.dns;
|
package io.netty.handler.codec.dns;
|
||||||
|
|
||||||
import io.netty.util.collection.IntObjectHashMap;
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a DNS record type.
|
* 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
|
* 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,
|
* to map hostnames to an IP address of the host, but also used for DNSBLs,
|
||||||
* storing subnet masks in RFC 1101, etc.
|
* 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
|
* Name server record RFC 1035 Delegates a DNS zone to use the given
|
||||||
* authoritative name servers
|
* 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
|
* Canonical name record RFC 1035 Alias of one name to another: the DNS
|
||||||
* lookup will continue by retrying the lookup with the new name.
|
* 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
|
* 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,
|
* server, the email of the domain administrator, the domain serial number,
|
||||||
* and several timers relating to refreshing the zone.
|
* 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
|
* 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
|
* use is for implementing reverse DNS lookups, but other uses include such
|
||||||
* things as DNS-SD.
|
* 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
|
* Mail exchange record RFC 1035 Maps a domain name to a list of message
|
||||||
* transfer agents for that domain.
|
* 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
|
* 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,
|
* opportunistic encryption, Sender Policy Framework, DKIM, DMARC DNS-SD,
|
||||||
* etc.
|
* 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
|
* Responsible person record RFC 1183 Information about the responsible
|
||||||
* person(s) for the domain. Usually an email address with the @ replaced by
|
* person(s) for the domain. Usually an email address with the @ replaced by
|
||||||
* a .
|
* 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.
|
* 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
|
* their local domain. A subtype of this record is used by the obsolete
|
||||||
* DCE/DFS file system.
|
* 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
|
* 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
|
* TKEY (RFC 2930). RFC 3755 designated RRSIG as the replacement for SIG for
|
||||||
* use within DNSSEC.
|
* 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
|
* 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 within DNSSEC. RFC 4025 designates IPSECKEY as the
|
||||||
* replacement for use with IPsec.
|
* 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
|
* IPv6 address record RFC 3596 Returns a 128-bit IPv6 address, most
|
||||||
* commonly used to map hostnames to an IP address of the host.
|
* 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
|
* Location record RFC 1876 Specifies a geographical location associated
|
||||||
* with a domain name.
|
* 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
|
* Service locator RFC 2782 Generalized service location record, used for
|
||||||
* newer protocols instead of creating protocol-specific records such as MX.
|
* 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
|
* Naming Authority Pointer record RFC 3403 Allows regular expression based
|
||||||
* rewriting of domain names which can then be used as URIs, further domain
|
* rewriting of domain names which can then be used as URIs, further domain
|
||||||
* names to lookups, etc.
|
* 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
|
* 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
|
* Informational status, rather than being on the IETF standards-track. It
|
||||||
* has always had limited deployment, but is still in use.
|
* 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.
|
* 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
|
* 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
|
* label. Like the CNAME record, the DNS lookup will continue by retrying
|
||||||
* the lookup with the new name.
|
* 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
|
* Option record RFC 2671 This is a pseudo DNS record type needed to support
|
||||||
* EDNS.
|
* 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.
|
* Address Prefix List record RFC 3123 Specify lists of address ranges, e.g.
|
||||||
* in CIDR format, for various address families. Experimental.
|
* 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
|
* Delegation signer record RFC 4034 The record used to identify the DNSSEC
|
||||||
* signing key of a delegated zone.
|
* 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
|
* 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
|
* 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.
|
* 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.
|
* 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
|
* DNSSEC signature record RFC 4034 Signature for a DNSSEC-secured record
|
||||||
* set. Uses the same format as the SIG 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
|
* 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.
|
* 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
|
* DNS Key record RFC 4034 The key record used in DNSSEC. Uses the same
|
||||||
* format as the KEY record.
|
* 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
|
* DHCP identifier record RFC 4701 Used in conjunction with the FQDN option
|
||||||
* to DHCP.
|
* 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
|
* NSEC record version 3 RFC 5155 An extension to DNSSEC that allows proof
|
||||||
* of nonexistence for a name without permitting zonewalking.
|
* 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.
|
* 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
|
* 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
|
* key with the domain name where the record is found, thus forming a 'TLSA
|
||||||
* certificate association'.
|
* 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
|
* Host Identity Protocol record RFC 5205 Method of separating the end-point
|
||||||
* identifier and locator roles of IP addresses.
|
* 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
|
* 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
|
* protocol as an alternative to of storing SPF data in TXT records. Uses
|
||||||
* the same format as the earlier TXT record.
|
* 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
|
* 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
|
* used with TSIG that is encrypted under the public key in an accompanying
|
||||||
* KEY RR..
|
* 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
|
* Transaction Signature record RFC 2845 Can be used to authenticate dynamic
|
||||||
* updates as coming from an approved client, or to authenticate responses
|
* updates as coming from an approved client, or to authenticate responses
|
||||||
* as coming from an approved recursive name server similar to DNSSEC.
|
* 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
|
* 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
|
* authoritative server is unable to fulfill the request due to
|
||||||
* configuration or lack of required deltas.
|
* 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
|
* Authoritative Zone Transfer record RFC 1035 Transfer entire zone file
|
||||||
* from the master name server to secondary name servers.
|
* 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
|
* 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
|
* returned. Sometimes referred to as ANY, for example in Windows nslookup
|
||||||
* and Wireshark.
|
* 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,
|
* Certification Authority Authorization record RFC 6844 CA pinning,
|
||||||
* constraining acceptable CAs for a host/domain.
|
* 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 Trust Authorities record N/A Part of a deployment proposal for
|
||||||
* DNSSEC without a signed DNS root. See the IANA database and Weiler Spec
|
* DNSSEC without a signed DNS root. See the IANA database and Weiler Spec
|
||||||
* for details. Uses the same format as the DS record.
|
* 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
|
* DNSSEC Lookaside Validation record RFC 4431 For publishing DNSSEC trust
|
||||||
* anchors outside of the DNS delegation chain. Uses the same format as the
|
* anchors outside of the DNS delegation chain. Uses the same format as the
|
||||||
* DS record. RFC 5074 describes a way of using these records.
|
* 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 Map<String, DnsRecordType> BY_NAME = new HashMap<String, DnsRecordType>();
|
||||||
private static final IntObjectHashMap<DnsType> BY_TYPE = new IntObjectHashMap<DnsType>();
|
private static final IntObjectHashMap<DnsRecordType> BY_TYPE = new IntObjectHashMap<DnsRecordType>();
|
||||||
private static final String EXPECTED;
|
private static final String EXPECTED;
|
||||||
|
|
||||||
static {
|
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,
|
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,
|
DS, SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, HIP, SPF, TKEY, TSIG, IXFR,
|
||||||
AXFR, ANY, CAA, TA, DLV
|
AXFR, ANY, CAA, TA, DLV
|
||||||
};
|
};
|
||||||
|
|
||||||
StringBuilder expected = new StringBuilder(512);
|
final StringBuilder expected = new StringBuilder(512);
|
||||||
expected.append(" (expected: ");
|
|
||||||
|
|
||||||
for (DnsType type: all) {
|
expected.append(" (expected: ");
|
||||||
|
for (DnsRecordType type: all) {
|
||||||
BY_NAME.put(type.name(), type);
|
BY_NAME.put(type.name(), type);
|
||||||
BY_TYPE.put(type.intValue(), type);
|
BY_TYPE.put(type.intValue(), type);
|
||||||
expected.append(type.name());
|
|
||||||
expected.append('(');
|
expected.append(type.name())
|
||||||
expected.append(type.intValue());
|
.append('(')
|
||||||
expected.append("), ");
|
.append(type.intValue())
|
||||||
|
.append("), ");
|
||||||
}
|
}
|
||||||
|
|
||||||
expected.setLength(expected.length() - 2);
|
expected.setLength(expected.length() - 2);
|
||||||
@ -328,33 +330,31 @@ public final class DnsType implements Comparable<DnsType> {
|
|||||||
EXPECTED = expected.toString();
|
EXPECTED = expected.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DnsType valueOf(int intValue) {
|
public static DnsRecordType valueOf(int intValue) {
|
||||||
DnsType result = BY_TYPE.get(intValue);
|
DnsRecordType result = BY_TYPE.get(intValue);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return new DnsType(intValue, "UNKNOWN");
|
return new DnsRecordType(intValue);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DnsType valueOf(String name) {
|
public static DnsRecordType valueOf(String name) {
|
||||||
DnsType result = BY_NAME.get(name);
|
DnsRecordType result = BY_NAME.get(name);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new IllegalArgumentException("name: " + name + EXPECTED);
|
throw new IllegalArgumentException("name: " + name + EXPECTED);
|
||||||
}
|
}
|
||||||
return result;
|
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 int intValue;
|
||||||
private final String name;
|
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) {
|
if ((intValue & 0xffff) != intValue) {
|
||||||
throw new IllegalArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)");
|
throw new IllegalArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)");
|
||||||
}
|
}
|
||||||
@ -383,16 +383,20 @@ public final class DnsType implements Comparable<DnsType> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof DnsType && ((DnsType) o).intValue == intValue;
|
return o instanceof DnsRecordType && ((DnsRecordType) o).intValue == intValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(DnsType o) {
|
public int compareTo(DnsRecordType o) {
|
||||||
return intValue() - o.intValue();
|
return intValue() - o.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -15,86 +15,99 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
package io.netty.handler.codec.dns;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DNS response packet which is sent to a client after a server receives a
|
* A DNS response message.
|
||||||
* query.
|
|
||||||
*/
|
*/
|
||||||
public final class DnsResponse extends DnsMessage {
|
public interface 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link InetSocketAddress} of the sender of this {@link DnsResponse}
|
* Returns {@code true} if responding server is authoritative for the domain
|
||||||
|
* name in the query message.
|
||||||
*/
|
*/
|
||||||
public InetSocketAddress sender() {
|
boolean isAuthoritativeAnswer();
|
||||||
return sender;
|
|
||||||
}
|
/**
|
||||||
|
* Set to {@code true} if responding server is authoritative for the domain
|
||||||
|
* name in the query message.
|
||||||
|
*
|
||||||
|
* @param authoritativeAnswer flag for authoritative answer
|
||||||
|
*/
|
||||||
|
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
|
@Override
|
||||||
public DnsResponse addAnswer(DnsResource answer) {
|
DnsResponse setId(int id);
|
||||||
super.addAnswer(answer);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse addQuestion(DnsQuestion question) {
|
DnsResponse setOpCode(DnsOpCode opCode);
|
||||||
super.addQuestion(question);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse addAuthorityResource(DnsResource resource) {
|
DnsResponse setRecursionDesired(boolean recursionDesired);
|
||||||
super.addAuthorityResource(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse addAdditionalResource(DnsResource resource) {
|
DnsResponse setZ(int z);
|
||||||
super.addAdditionalResource(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse touch(Object hint) {
|
DnsResponse setRecord(DnsSection section, DnsRecord record);
|
||||||
super.touch(hint);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse retain() {
|
DnsResponse addRecord(DnsSection section, DnsRecord record);
|
||||||
super.retain();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse retain(int increment) {
|
DnsResponse addRecord(DnsSection section, int index, DnsRecord record);
|
||||||
super.retain(increment);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponse touch() {
|
DnsResponse clear(DnsSection section);
|
||||||
super.touch();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DnsResponseHeader header() {
|
DnsResponse clear();
|
||||||
return (DnsResponseHeader) super.header();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DnsResponseHeader newHeader(int id) {
|
DnsResponse touch();
|
||||||
return new DnsResponseHeader(this, id);
|
|
||||||
}
|
@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,
|
* 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
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -15,159 +15,183 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
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
|
* The DNS {@code RCODE}, as defined in <a href="https://tools.ietf.org/html/rfc2929">RFC2929</a>.
|
||||||
* query. A response code of 0 indicates no error.
|
|
||||||
*/
|
*/
|
||||||
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");
|
public static final DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE");
|
||||||
|
|
||||||
private final int errorCode;
|
|
||||||
private final String message;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link DnsResponseCode} that corresponds with the given
|
* The 'BADNAME' DNS RCODE (20), as defined in <a href="https://tools.ietf.org/html/rfc2930">RFC2930</a>.
|
||||||
* {@code responseCode}.
|
*/
|
||||||
|
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
|
* @param responseCode the DNS RCODE
|
||||||
* the error code's id
|
*
|
||||||
* @return corresponding {@link DnsResponseCode} or {@code null} if none can be found.
|
* @return the corresponding {@link DnsResponseCode}
|
||||||
*/
|
*/
|
||||||
public static DnsResponseCode valueOf(int responseCode) {
|
public static DnsResponseCode valueOf(int responseCode) {
|
||||||
switch (responseCode) {
|
switch (responseCode) {
|
||||||
case 0:
|
case 0:
|
||||||
return NOERROR;
|
return NOERROR;
|
||||||
case 1:
|
case 1:
|
||||||
return FORMERROR;
|
return FORMERR;
|
||||||
case 2:
|
case 2:
|
||||||
return SERVFAIL;
|
return SERVFAIL;
|
||||||
case 3:
|
case 3:
|
||||||
return NXDOMAIN;
|
return NXDOMAIN;
|
||||||
case 4:
|
case 4:
|
||||||
return NOTIMPL;
|
return NOTIMP;
|
||||||
case 5:
|
case 5:
|
||||||
return REFUSED;
|
return REFUSED;
|
||||||
case 6:
|
case 6:
|
||||||
return YXDOMAIN;
|
return YXDOMAIN;
|
||||||
case 7:
|
case 7:
|
||||||
return YXRRSET;
|
return YXRRSET;
|
||||||
case 8:
|
case 8:
|
||||||
return NXRRSET;
|
return NXRRSET;
|
||||||
case 9:
|
case 9:
|
||||||
return NOTAUTH;
|
return NOTAUTH;
|
||||||
case 10:
|
case 10:
|
||||||
return NOTZONE;
|
return NOTZONE;
|
||||||
case 11:
|
case 16:
|
||||||
return BADVERS;
|
return BADVERS_OR_BADSIG;
|
||||||
case 12:
|
case 17:
|
||||||
return BADSIG;
|
return BADKEY;
|
||||||
case 13:
|
case 18:
|
||||||
return BADKEY;
|
return BADTIME;
|
||||||
case 14:
|
case 19:
|
||||||
return BADTIME;
|
return BADMODE;
|
||||||
default:
|
case 20:
|
||||||
return new DnsResponseCode(responseCode, null);
|
return BADNAME;
|
||||||
|
case 21:
|
||||||
|
return BADALG;
|
||||||
|
default:
|
||||||
|
return new DnsResponseCode(responseCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DnsResponseCode(int errorCode, String message) {
|
private final int code;
|
||||||
this.errorCode = errorCode;
|
private final String name;
|
||||||
this.message = message;
|
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}.
|
* Returns the error code for this {@link DnsResponseCode}.
|
||||||
*/
|
*/
|
||||||
public int code() {
|
public int intValue() {
|
||||||
return errorCode;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(DnsResponseCode o) {
|
public int compareTo(DnsResponseCode o) {
|
||||||
return code() - o.code();
|
return intValue() - o.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
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
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
@ -175,7 +199,7 @@ public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return code() == ((DnsResponseCode) o).code();
|
return intValue() == ((DnsResponseCode) o).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,9 +207,10 @@ public final class DnsResponseCode implements Comparable<DnsResponseCode> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (message == null) {
|
String text = this.text;
|
||||||
return "DnsResponseCode(" + errorCode + ')';
|
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,
|
* 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
|
* 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.
|
* DNS codec.
|
||||||
* Includes decoders and classes for representing messages and resources.
|
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.dns;
|
package io.netty.handler.codec.dns;
|
||||||
|
@ -25,29 +25,40 @@ import java.net.InetSocketAddress;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
public class DnsQueryTest {
|
public class DnsQueryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeQueryTest() throws Exception {
|
public void writeQueryTest() throws Exception {
|
||||||
InetSocketAddress addr = new InetSocketAddress(0);
|
InetSocketAddress addr = new InetSocketAddress(0);
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(new DnsQueryEncoder());
|
EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsQueryEncoder());
|
||||||
List<DnsQuery> queries = new ArrayList<DnsQuery>(5);
|
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 DatagramDnsQuery(addr, null, 1).setRecord(
|
||||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("www.example.com", DnsType.A)));
|
DnsSection.QUESTION,
|
||||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.AAAA)));
|
new DefaultDnsQuestion("1.0.0.127.in-addr.arpa", DnsRecordType.PTR)));
|
||||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.MX)));
|
queries.add(new DatagramDnsQuery(addr, null, 1).setRecord(
|
||||||
queries.add(new DnsQuery(1, addr).addQuestion(new DnsQuestion("example.com", DnsType.CNAME)));
|
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) {
|
for (DnsQuery query: queries) {
|
||||||
Assert.assertEquals("Invalid question count, expected 1.", 1, query.header().questionCount());
|
assertThat(query.count(DnsSection.QUESTION), is(1));
|
||||||
Assert.assertEquals("Invalid answer count, expected 0.", 0, query.header().answerCount());
|
assertThat(query.count(DnsSection.ANSWER), is(0));
|
||||||
Assert.assertEquals("Invalid authority resource record count, expected 0.", 0, query.header()
|
assertThat(query.count(DnsSection.AUTHORITY), is(0));
|
||||||
.authorityResourceCount());
|
assertThat(query.count(DnsSection.ADDITIONAL), is(0));
|
||||||
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());
|
|
||||||
embedder.writeOutbound(query);
|
embedder.writeOutbound(query);
|
||||||
|
|
||||||
DatagramPacket packet = embedder.readOutbound();
|
DatagramPacket packet = embedder.readOutbound();
|
||||||
Assert.assertTrue(packet.content().isReadable());
|
Assert.assertTrue(packet.content().isReadable());
|
||||||
packet.release();
|
packet.release();
|
||||||
|
@ -25,13 +25,13 @@ import java.util.List;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class DnsClassTest {
|
public class DnsRecordTypeTest {
|
||||||
|
|
||||||
private static List<DnsClass> allTypes() throws Exception {
|
private static List<DnsRecordType> allTypes() throws Exception {
|
||||||
List<DnsClass> result = new ArrayList<DnsClass>();
|
List<DnsRecordType> result = new ArrayList<DnsRecordType>();
|
||||||
for (Field field : DnsClass.class.getDeclaredFields()) {
|
for (Field field : DnsRecordType.class.getFields()) {
|
||||||
if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsClass.class) {
|
if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsRecordType.class) {
|
||||||
result.add((DnsClass) field.get(null));
|
result.add((DnsRecordType) field.get(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertFalse(result.isEmpty());
|
assertFalse(result.isEmpty());
|
||||||
@ -41,26 +41,26 @@ public class DnsClassTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testSanity() throws Exception {
|
public void testSanity() throws Exception {
|
||||||
assertEquals("More than one type has the same int value",
|
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
|
@Test
|
||||||
public void testHashCode() throws Exception {
|
public void testHashCode() throws Exception {
|
||||||
for (DnsClass t : allTypes()) {
|
for (DnsRecordType t : allTypes()) {
|
||||||
assertEquals(t.intValue(), t.hashCode());
|
assertEquals(t.intValue(), t.hashCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test of equals method, of class DnsClass.
|
* Test of equals method, of class DnsRecordType.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEquals() throws Exception {
|
public void testEquals() throws Exception {
|
||||||
for (DnsClass t1 : allTypes()) {
|
for (DnsRecordType t1 : allTypes()) {
|
||||||
for (DnsClass t2 : allTypes()) {
|
for (DnsRecordType t2 : allTypes()) {
|
||||||
if (t1 != t2) {
|
if (t1 != t2) {
|
||||||
assertNotEquals(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
|
@Test
|
||||||
public void testFind() throws Exception {
|
public void testFind() throws Exception {
|
||||||
for (DnsClass t : allTypes()) {
|
for (DnsRecordType t : allTypes()) {
|
||||||
DnsClass found = DnsClass.valueOf(t.intValue());
|
DnsRecordType found = DnsRecordType.valueOf(t.intValue());
|
||||||
assertSame(t, found);
|
assertSame(t, found);
|
||||||
found = DnsClass.valueOf(t.toString());
|
found = DnsRecordType.valueOf(t.name());
|
||||||
assertSame(t.toString(), t, found);
|
assertSame(t.name(), t, found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,16 +17,19 @@ package io.netty.handler.codec.dns;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.AddressedEnvelope;
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.channel.socket.DatagramPacket;
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class DnsResponseTest {
|
public class DnsResponseTest {
|
||||||
|
|
||||||
private static final byte[][] packets = {
|
private static final byte[][] packets = {
|
||||||
@ -69,25 +72,23 @@ public class DnsResponseTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readResponseTest() throws Exception {
|
public void readResponseTest() throws Exception {
|
||||||
EmbeddedChannel embedder = new EmbeddedChannel(new DnsResponseDecoder());
|
EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder());
|
||||||
for (byte[] p: packets) {
|
for (byte[] p: packets) {
|
||||||
ByteBuf packet = embedder.alloc().buffer(512).writeBytes(p);
|
ByteBuf packet = embedder.alloc().buffer(512).writeBytes(p);
|
||||||
embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)));
|
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);
|
ByteBuf raw = Unpooled.wrappedBuffer(p);
|
||||||
Assert.assertEquals("Invalid id, expected: " + raw.getUnsignedShort(0) + ", actual: "
|
assertThat(response.id(), is(raw.getUnsignedShort(0)));
|
||||||
+ decoded.header().id(), raw.getUnsignedShort(0), decoded.header().id());
|
assertThat(response.count(DnsSection.QUESTION), is(raw.getUnsignedShort(4)));
|
||||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(4) + ", actual: "
|
assertThat(response.count(DnsSection.ANSWER), is(raw.getUnsignedShort(6)));
|
||||||
+ decoded.questions().size(), raw.getUnsignedShort(4), decoded.questions().size());
|
assertThat(response.count(DnsSection.AUTHORITY), is(raw.getUnsignedShort(8)));
|
||||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(6) + ", actual: "
|
assertThat(response.count(DnsSection.ADDITIONAL), is(raw.getUnsignedShort(10)));
|
||||||
+ decoded.answers().size(), raw.getUnsignedShort(6), decoded.answers().size());
|
|
||||||
Assert.assertEquals("Invalid resource count, expected: " + raw.getUnsignedShort(8) + ", actual: "
|
envelope.release();
|
||||||
+ 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ public class DnsResponseTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readMalormedResponseTest() throws Exception {
|
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);
|
ByteBuf packet = embedder.alloc().buffer(512).writeBytes(malformedLoopPacket);
|
||||||
exception.expect(CorruptedFrameException.class);
|
exception.expect(CorruptedFrameException.class);
|
||||||
embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)));
|
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 final class StringUtil {
|
||||||
|
|
||||||
|
public static final String EMPTY_STRING = "";
|
||||||
public static final String NEWLINE;
|
public static final String NEWLINE;
|
||||||
|
|
||||||
public static final char DOUBLE_QUOTE = '\"';
|
public static final char DOUBLE_QUOTE = '\"';
|
||||||
public static final char COMMA = ',';
|
public static final char COMMA = ',';
|
||||||
public static final char LINE_FEED = '\n';
|
public static final char LINE_FEED = '\n';
|
||||||
public static final char CARRIAGE_RETURN = '\r';
|
public static final char CARRIAGE_RETURN = '\r';
|
||||||
public static final String EMPTY_STRING = "";
|
public static final char TAB = '\t';
|
||||||
public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = (int) 'a' - (int) 'A';
|
|
||||||
|
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_PAD = new String[256];
|
||||||
private static final String[] BYTE2HEX_NOPAD = new String[256];
|
private static final String[] BYTE2HEX_NOPAD = new String[256];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2 - Quote character at beginning and end.
|
* 2 - Quote character at beginning and end.
|
||||||
* 5 - Extra allowance for anticipated escape characters that may be added.
|
* 5 - Extra allowance for anticipated escape characters that may be added.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.resolver.dns;
|
package io.netty.resolver.dns;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.AddressedEnvelope;
|
||||||
import io.netty.channel.ChannelFactory;
|
import io.netty.channel.ChannelFactory;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
@ -27,13 +28,14 @@ import io.netty.channel.FixedRecvByteBufAllocator;
|
|||||||
import io.netty.channel.ReflectiveChannelFactory;
|
import io.netty.channel.ReflectiveChannelFactory;
|
||||||
import io.netty.channel.socket.DatagramChannel;
|
import io.netty.channel.socket.DatagramChannel;
|
||||||
import io.netty.channel.socket.InternetProtocolFamily;
|
import io.netty.channel.socket.InternetProtocolFamily;
|
||||||
import io.netty.handler.codec.dns.DnsClass;
|
import io.netty.handler.codec.dns.DatagramDnsQueryEncoder;
|
||||||
import io.netty.handler.codec.dns.DnsQueryEncoder;
|
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.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.DnsResponse;
|
||||||
import io.netty.handler.codec.dns.DnsResponseCode;
|
import io.netty.handler.codec.dns.DnsResponseCode;
|
||||||
import io.netty.handler.codec.dns.DnsResponseDecoder;
|
|
||||||
import io.netty.resolver.NameResolver;
|
import io.netty.resolver.NameResolver;
|
||||||
import io.netty.resolver.SimpleNameResolver;
|
import io.netty.resolver.SimpleNameResolver;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
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 DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder();
|
||||||
private static final DnsQueryEncoder ENCODER = new DnsQueryEncoder();
|
private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder();
|
||||||
|
|
||||||
final Iterable<InetSocketAddress> nameServerAddresses;
|
final Iterable<InetSocketAddress> nameServerAddresses;
|
||||||
final ChannelFuture bindFuture;
|
final ChannelFuture bindFuture;
|
||||||
@ -117,7 +119,6 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
private volatile int maxQueriesPerResolve = 8;
|
private volatile int maxQueriesPerResolve = 8;
|
||||||
|
|
||||||
private volatile int maxPayloadSize;
|
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.
|
* 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;
|
this.maxPayloadSize = maxPayloadSize;
|
||||||
maxPayloadSizeClass = DnsClass.valueOf(maxPayloadSize);
|
|
||||||
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
|
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DnsClass maxPayloadSizeClass() {
|
|
||||||
return maxPayloadSizeClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all the DNS resource records cached by this resolver.
|
* 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.
|
* 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);
|
return query(nameServerAddresses, question);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a DNS query with the specified 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);
|
return query(nameServerAddresses, question, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a DNS query with the specified question using the specified name server list.
|
* Sends a DNS query with the specified question using the specified name server list.
|
||||||
*/
|
*/
|
||||||
public Future<DnsResponse> query(Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question) {
|
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||||
|
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question) {
|
||||||
if (nameServerAddresses == null) {
|
if (nameServerAddresses == null) {
|
||||||
throw new NullPointerException("nameServerAddresses");
|
throw new NullPointerException("nameServerAddresses");
|
||||||
}
|
}
|
||||||
@ -693,15 +691,18 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
return eventLoop.newFailedFuture(cachedResult.cause);
|
return eventLoop.newFailedFuture(cachedResult.cause);
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
* Sends a DNS query with the specified question using the specified name server list.
|
||||||
*/
|
*/
|
||||||
public Future<DnsResponse> query(
|
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question, Promise<DnsResponse> promise) {
|
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question,
|
||||||
|
Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||||
|
|
||||||
if (nameServerAddresses == null) {
|
if (nameServerAddresses == null) {
|
||||||
throw new NullPointerException("nameServerAddresses");
|
throw new NullPointerException("nameServerAddresses");
|
||||||
@ -716,23 +717,25 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
final DnsCacheEntry cachedResult = queryCache.get(question);
|
final DnsCacheEntry cachedResult = queryCache.get(question);
|
||||||
if (cachedResult != null) {
|
if (cachedResult != null) {
|
||||||
if (cachedResult.response != null) {
|
if (cachedResult.response != null) {
|
||||||
return promise.setSuccess(cachedResult.response.retain());
|
return cast(promise).setSuccess(cachedResult.response.retain());
|
||||||
} else {
|
} else {
|
||||||
return promise.setFailure(cachedResult.cause);
|
return cast(promise).setFailure(cachedResult.cause);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return query0(nameServerAddresses, question, promise);
|
return query0(nameServerAddresses, question, promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<DnsResponse> query0(
|
private Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query0(
|
||||||
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question, Promise<DnsResponse> promise) {
|
Iterable<InetSocketAddress> nameServerAddresses, DnsQuestion question,
|
||||||
|
Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||||
|
|
||||||
|
final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> castPromise = cast(promise);
|
||||||
try {
|
try {
|
||||||
new DnsQueryContext(this, nameServerAddresses, question, promise).query();
|
new DnsQueryContext(this, nameServerAddresses, question, castPromise).query();
|
||||||
return promise;
|
return castPromise;
|
||||||
} catch (Exception e) {
|
} 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 {
|
private final class DnsResponseHandler extends ChannelInboundHandlerAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
try {
|
try {
|
||||||
final DnsResponse res = (DnsResponse) msg;
|
final DatagramDnsResponse res = (DatagramDnsResponse) msg;
|
||||||
final int queryId = res.header().id();
|
final int queryId = res.id();
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} RECEIVED: [{}: {}], {}", ch, queryId, res.sender(), res);
|
logger.debug("{} RECEIVED: [{}: {}], {}", ch, queryId, res.sender(), res);
|
||||||
@ -782,14 +795,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<DnsQuestion> questions = res.questions();
|
if (res.count(DnsSection.QUESTION) != 1) {
|
||||||
if (questions.size() != 1) {
|
|
||||||
logger.warn("Received a DNS response with invalid number of questions: {}", res);
|
logger.warn("Received a DNS response with invalid number of questions: {}", res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DnsQuestion q = qCtx.question();
|
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);
|
logger.warn("Received a mismatching DNS response: {}", res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -800,26 +812,26 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
timeoutFuture.cancel(false);
|
timeoutFuture.cancel(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.header().responseCode() == DnsResponseCode.NOERROR) {
|
if (res.code() == DnsResponseCode.NOERROR) {
|
||||||
cache(q, res);
|
cache(q, res);
|
||||||
promises.set(queryId, null);
|
promises.set(queryId, null);
|
||||||
|
|
||||||
Promise<DnsResponse> qPromise = qCtx.promise();
|
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> qPromise = qCtx.promise();
|
||||||
if (qPromise.setUncancellable()) {
|
if (qPromise.setUncancellable()) {
|
||||||
qPromise.setSuccess(res.retain());
|
qPromise.setSuccess(cast(res.retain()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCtx.retry(res.sender(),
|
qCtx.retry(res.sender(),
|
||||||
"response code: " + res.header().responseCode() +
|
"response code: " + res.code() +
|
||||||
" with " + res.answers().size() + " answer(s) and " +
|
" with " + res.count(DnsSection.ANSWER) + " answer(s) and " +
|
||||||
res.authorityResources().size() + " authority resource(s)");
|
res.count(DnsSection.AUTHORITY) + " authority resource(s)");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
ReferenceCountUtil.safeRelease(msg);
|
ReferenceCountUtil.safeRelease(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cache(DnsQuestion question, DnsResponse res) {
|
private void cache(DnsQuestion question, AddressedEnvelope<? extends DnsResponse, InetSocketAddress> res) {
|
||||||
final int maxTtl = maxTtl();
|
final int maxTtl = maxTtl();
|
||||||
if (maxTtl == 0) {
|
if (maxTtl == 0) {
|
||||||
return;
|
return;
|
||||||
@ -827,8 +839,11 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
|
|
||||||
long ttl = Long.MAX_VALUE;
|
long ttl = Long.MAX_VALUE;
|
||||||
// Find the smallest TTL value returned by the server.
|
// Find the smallest TTL value returned by the server.
|
||||||
for (DnsResource r: res.answers()) {
|
final DnsResponse resc = res.content();
|
||||||
long rTtl = r.timeToLive();
|
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) {
|
if (ttl > rTtl) {
|
||||||
ttl = rTtl;
|
ttl = rTtl;
|
||||||
}
|
}
|
||||||
@ -847,12 +862,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final class DnsCacheEntry {
|
static final class DnsCacheEntry {
|
||||||
final DnsResponse response;
|
final AddressedEnvelope<DnsResponse, InetSocketAddress> response;
|
||||||
final Throwable cause;
|
final Throwable cause;
|
||||||
volatile ScheduledFuture<?> expirationFuture;
|
volatile ScheduledFuture<?> expirationFuture;
|
||||||
|
|
||||||
DnsCacheEntry(DnsResponse response) {
|
@SuppressWarnings("unchecked")
|
||||||
this.response = response.retain();
|
DnsCacheEntry(AddressedEnvelope<? extends DnsResponse, InetSocketAddress> response) {
|
||||||
|
this.response = (AddressedEnvelope<DnsResponse, InetSocketAddress>) response.retain();
|
||||||
cause = null;
|
cause = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,7 +878,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void release() {
|
void release() {
|
||||||
DnsResponse response = this.response;
|
AddressedEnvelope<DnsResponse, InetSocketAddress> response = this.response;
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
ReferenceCountUtil.safeRelease(response);
|
ReferenceCountUtil.safeRelease(response);
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,17 @@
|
|||||||
package io.netty.resolver.dns;
|
package io.netty.resolver.dns;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
|
import io.netty.channel.AddressedEnvelope;
|
||||||
import io.netty.channel.socket.InternetProtocolFamily;
|
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.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.DnsResponse;
|
||||||
import io.netty.handler.codec.dns.DnsResponseDecoder;
|
|
||||||
import io.netty.handler.codec.dns.DnsType;
|
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
@ -51,14 +55,15 @@ final class DnsNameResolverContext {
|
|||||||
private static final int INADDRSZ4 = 4;
|
private static final int INADDRSZ4 = 4;
|
||||||
private static final int INADDRSZ6 = 16;
|
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 =
|
||||||
@Override
|
new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() {
|
||||||
public void operationComplete(Future<DnsResponse> future) {
|
@Override
|
||||||
if (future.isSuccess()) {
|
public void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) {
|
||||||
future.getNow().release();
|
if (future.isSuccess()) {
|
||||||
}
|
future.getNow().release();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final DnsNameResolver parent;
|
private final DnsNameResolver parent;
|
||||||
private final Promise<InetSocketAddress> promise;
|
private final Promise<InetSocketAddress> promise;
|
||||||
@ -67,8 +72,10 @@ final class DnsNameResolverContext {
|
|||||||
private final int maxAllowedQueries;
|
private final int maxAllowedQueries;
|
||||||
private final InternetProtocolFamily[] resolveAddressTypes;
|
private final InternetProtocolFamily[] resolveAddressTypes;
|
||||||
|
|
||||||
private final Set<Future<DnsResponse>> queriesInProgress =
|
private final Set<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> queriesInProgress =
|
||||||
Collections.newSetFromMap(new IdentityHashMap<Future<DnsResponse>, Boolean>());
|
Collections.newSetFromMap(
|
||||||
|
new IdentityHashMap<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>, Boolean>());
|
||||||
|
|
||||||
private List<InetAddress> resolvedAddresses;
|
private List<InetAddress> resolvedAddresses;
|
||||||
private StringBuilder trace;
|
private StringBuilder trace;
|
||||||
private int allowedQueries;
|
private int allowedQueries;
|
||||||
@ -87,19 +94,19 @@ final class DnsNameResolverContext {
|
|||||||
|
|
||||||
void resolve() {
|
void resolve() {
|
||||||
for (InternetProtocolFamily f: resolveAddressTypes) {
|
for (InternetProtocolFamily f: resolveAddressTypes) {
|
||||||
final DnsType type;
|
final DnsRecordType type;
|
||||||
switch (f) {
|
switch (f) {
|
||||||
case IPv4:
|
case IPv4:
|
||||||
type = DnsType.A;
|
type = DnsRecordType.A;
|
||||||
break;
|
break;
|
||||||
case IPv6:
|
case IPv6:
|
||||||
type = DnsType.AAAA;
|
type = DnsRecordType.AAAA;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
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 --;
|
allowedQueries --;
|
||||||
|
|
||||||
final Future<DnsResponse> f = parent.query(nameServerAddresses, question);
|
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = parent.query(nameServerAddresses, question);
|
||||||
queriesInProgress.add(f);
|
queriesInProgress.add(f);
|
||||||
|
|
||||||
f.addListener(new FutureListener<DnsResponse>() {
|
f.addListener(new FutureListener<AddressedEnvelope<DnsResponse, InetSocketAddress>>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<DnsResponse> future) throws Exception {
|
public void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> future) {
|
||||||
queriesInProgress.remove(future);
|
queriesInProgress.remove(future);
|
||||||
|
|
||||||
if (promise.isDone()) {
|
if (promise.isDone()) {
|
||||||
@ -135,12 +142,12 @@ final class DnsNameResolverContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResponse(final DnsQuestion question, final DnsResponse response) {
|
void onResponse(final DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> response) {
|
||||||
final DnsType type = question.type();
|
final DnsRecordType type = question.type();
|
||||||
try {
|
try {
|
||||||
if (type == DnsType.A || type == DnsType.AAAA) {
|
if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
|
||||||
onResponseAorAAAA(type, question, response);
|
onResponseAorAAAA(type, question, response);
|
||||||
} else if (type == DnsType.CNAME) {
|
} else if (type == DnsRecordType.CNAME) {
|
||||||
onResponseCNAME(question, response);
|
onResponseCNAME(question, response);
|
||||||
}
|
}
|
||||||
} finally {
|
} 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.
|
// We often get a bunch of CNAMES as well when we asked for A/AAAA.
|
||||||
|
final DnsResponse response = envelope.content();
|
||||||
final Map<String, String> cnames = buildAliasMap(response);
|
final Map<String, String> cnames = buildAliasMap(response);
|
||||||
|
final int answerCount = response.count(DnsSection.ANSWER);
|
||||||
|
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (DnsResource r: response.answers()) {
|
for (int i = 0; i < answerCount; i ++) {
|
||||||
final DnsType type = r.type();
|
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||||
if (type != DnsType.A && type != DnsType.AAAA) {
|
final DnsRecordType type = r.type();
|
||||||
|
if (type != DnsRecordType.A && type != DnsRecordType.AAAA) {
|
||||||
continue;
|
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();
|
final int contentLen = content.readableBytes();
|
||||||
if (contentLen != INADDRSZ4 && contentLen != INADDRSZ6) {
|
if (contentLen != INADDRSZ4 && contentLen != INADDRSZ6) {
|
||||||
continue;
|
continue;
|
||||||
@ -204,20 +220,21 @@ final class DnsNameResolverContext {
|
|||||||
return;
|
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.
|
// We aked for A/AAAA but we got only CNAME.
|
||||||
if (!cnames.isEmpty()) {
|
if (!cnames.isEmpty()) {
|
||||||
onResponseCNAME(question, response, cnames, false);
|
onResponseCNAME(question, envelope, cnames, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onResponseCNAME(DnsQuestion question, DnsResponse response) {
|
private void onResponseCNAME(DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) {
|
||||||
onResponseCNAME(question, response, buildAliasMap(response), true);
|
onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onResponseCNAME(
|
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.
|
// Resolve the host name in the question into the real host name.
|
||||||
final String name = question.name().toLowerCase(Locale.US);
|
final String name = question.name().toLowerCase(Locale.US);
|
||||||
@ -241,15 +258,22 @@ final class DnsNameResolverContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, String> buildAliasMap(DnsResponse response) {
|
private static Map<String, String> buildAliasMap(DnsResponse response) {
|
||||||
|
final int answerCount = response.count(DnsSection.ANSWER);
|
||||||
Map<String, String> cnames = null;
|
Map<String, String> cnames = null;
|
||||||
for (DnsResource r: response.answers()) {
|
for (int i = 0; i < answerCount; i ++) {
|
||||||
final DnsType type = r.type();
|
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||||
if (type != DnsType.CNAME) {
|
final DnsRecordType type = r.type();
|
||||||
|
if (type != DnsRecordType.CNAME) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String content = decodeDomainName(r.content());
|
if (!(r instanceof DnsRawRecord)) {
|
||||||
if (content == null) {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ByteBuf recordContent = ((ByteBufHolder) r).content();
|
||||||
|
final String domainName = decodeDomainName(recordContent);
|
||||||
|
if (domainName == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +281,7 @@ final class DnsNameResolverContext {
|
|||||||
cnames = new HashMap<String, String>();
|
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();
|
return cnames != null? cnames : Collections.<String, String>emptyMap();
|
||||||
@ -281,7 +305,7 @@ final class DnsNameResolverContext {
|
|||||||
if (!triedCNAME) {
|
if (!triedCNAME) {
|
||||||
// As the last resort, try to query CNAME, just in case the name server has it.
|
// As the last resort, try to query CNAME, just in case the name server has it.
|
||||||
triedCNAME = true;
|
triedCNAME = true;
|
||||||
query(parent.nameServerAddresses, new DnsQuestion(hostname, DnsType.CNAME, DnsClass.IN));
|
query(parent.nameServerAddresses, new DefaultDnsQuestion(hostname, DnsRecordType.CNAME));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,8 +343,9 @@ final class DnsNameResolverContext {
|
|||||||
private void finishResolve() {
|
private void finishResolve() {
|
||||||
if (!queriesInProgress.isEmpty()) {
|
if (!queriesInProgress.isEmpty()) {
|
||||||
// If there are queries in progress, we should cancel it because we already finished the resolution.
|
// If there are queries in progress, we should cancel it because we already finished the resolution.
|
||||||
for (Iterator<Future<DnsResponse>> i = queriesInProgress.iterator(); i.hasNext();) {
|
for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator();
|
||||||
Future<DnsResponse> f = i.next();
|
i.hasNext();) {
|
||||||
|
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = i.next();
|
||||||
i.remove();
|
i.remove();
|
||||||
|
|
||||||
if (!f.cancel(false)) {
|
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) {
|
static String decodeDomainName(ByteBuf buf) {
|
||||||
buf.markReaderIndex();
|
buf.markReaderIndex();
|
||||||
try {
|
try {
|
||||||
int position = -1;
|
int position = -1;
|
||||||
int checked = 0;
|
int checked = 0;
|
||||||
int length = buf.writerIndex();
|
final int end = buf.writerIndex();
|
||||||
StringBuilder name = new StringBuilder(64);
|
final StringBuilder name = new StringBuilder(buf.readableBytes() << 1);
|
||||||
for (int len = buf.readUnsignedByte(); buf.isReadable() && len != 0; len = buf.readUnsignedByte()) {
|
for (int len = buf.readUnsignedByte(); buf.isReadable() && len != 0; len = buf.readUnsignedByte()) {
|
||||||
boolean pointer = (len & 0xc0) == 0xc0;
|
boolean pointer = (len & 0xc0) == 0xc0;
|
||||||
if (pointer) {
|
if (pointer) {
|
||||||
if (position == -1) {
|
if (position == -1) {
|
||||||
position = buf.readerIndex() + 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
|
// check for loops
|
||||||
checked += 2;
|
checked += 2;
|
||||||
if (checked >= length) {
|
if (checked >= end) {
|
||||||
// Name contains a loop; give up.
|
// Name contains a loop; give up.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -449,8 +481,8 @@ final class DnsNameResolverContext {
|
|||||||
trace.append(" CNAME ");
|
trace.append(" CNAME ");
|
||||||
trace.append(cname);
|
trace.append(cname);
|
||||||
|
|
||||||
query(parent.nameServerAddresses, new DnsQuestion(cname, DnsType.A, DnsClass.IN));
|
query(parent.nameServerAddresses, new DefaultDnsQuestion(cname, DnsRecordType.A));
|
||||||
query(parent.nameServerAddresses, new DnsQuestion(cname, DnsType.AAAA, DnsClass.IN));
|
query(parent.nameServerAddresses, new DefaultDnsQuestion(cname, DnsRecordType.AAAA));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTrace(InetSocketAddress nameServerAddr, String msg) {
|
private void addTrace(InetSocketAddress nameServerAddr, String msg) {
|
||||||
|
@ -17,13 +17,17 @@
|
|||||||
package io.netty.resolver.dns;
|
package io.netty.resolver.dns;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.AddressedEnvelope;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
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.DnsQuery;
|
||||||
import io.netty.handler.codec.dns.DnsQuestion;
|
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.DnsResponse;
|
||||||
import io.netty.handler.codec.dns.DnsType;
|
|
||||||
import io.netty.resolver.dns.DnsNameResolver.DnsCacheEntry;
|
import io.netty.resolver.dns.DnsNameResolver.DnsCacheEntry;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.ScheduledFuture;
|
import io.netty.util.concurrent.ScheduledFuture;
|
||||||
@ -43,10 +47,10 @@ final class DnsQueryContext {
|
|||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class);
|
||||||
|
|
||||||
private final DnsNameResolver parent;
|
private final DnsNameResolver parent;
|
||||||
private final Promise<DnsResponse> promise;
|
private final Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise;
|
||||||
private final int id;
|
private final int id;
|
||||||
private final DnsQuestion question;
|
private final DnsQuestion question;
|
||||||
private final DnsResource optResource;
|
private final DnsRecord optResource;
|
||||||
private final Iterator<InetSocketAddress> nameServerAddresses;
|
private final Iterator<InetSocketAddress> nameServerAddresses;
|
||||||
|
|
||||||
private final boolean recursionDesired;
|
private final boolean recursionDesired;
|
||||||
@ -57,7 +61,7 @@ final class DnsQueryContext {
|
|||||||
|
|
||||||
DnsQueryContext(DnsNameResolver parent,
|
DnsQueryContext(DnsNameResolver parent,
|
||||||
Iterable<InetSocketAddress> nameServerAddresses,
|
Iterable<InetSocketAddress> nameServerAddresses,
|
||||||
DnsQuestion question, Promise<DnsResponse> promise) {
|
DnsQuestion question, Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise) {
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
@ -67,7 +71,8 @@ final class DnsQueryContext {
|
|||||||
recursionDesired = parent.isRecursionDesired();
|
recursionDesired = parent.isRecursionDesired();
|
||||||
maxTries = parent.maxTriesPerQuery();
|
maxTries = parent.maxTriesPerQuery();
|
||||||
remainingTries = maxTries;
|
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();
|
this.nameServerAddresses = nameServerAddresses.iterator();
|
||||||
}
|
}
|
||||||
@ -89,7 +94,7 @@ final class DnsQueryContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise<DnsResponse> promise() {
|
Promise<AddressedEnvelope<DnsResponse, InetSocketAddress>> promise() {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,10 +130,10 @@ final class DnsQueryContext {
|
|||||||
remainingTries --;
|
remainingTries --;
|
||||||
|
|
||||||
final InetSocketAddress nameServerAddr = nameServerAddresses.next();
|
final InetSocketAddress nameServerAddr = nameServerAddresses.next();
|
||||||
final DnsQuery query = new DnsQuery(id, nameServerAddr);
|
final DatagramDnsQuery query = new DatagramDnsQuery(null, nameServerAddr, id);
|
||||||
query.addQuestion(question);
|
query.setRecursionDesired(recursionDesired);
|
||||||
query.header().setRecursionDesired(recursionDesired);
|
query.setRecord(DnsSection.QUESTION, question);
|
||||||
query.addAdditionalResource(optResource);
|
query.setRecord(DnsSection.ADDITIONAL, optResource);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} WRITE: [{}: {}], {}", parent.ch, id, nameServerAddr, question);
|
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,
|
* 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
|
* 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
|
* License for the specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.netty.resolver.dns;
|
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.EventLoopGroup;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.InternetProtocolFamily;
|
import io.netty.channel.socket.InternetProtocolFamily;
|
||||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||||
import io.netty.handler.codec.dns.DnsQuestion;
|
import io.netty.handler.codec.dns.DefaultDnsQuestion;
|
||||||
import io.netty.handler.codec.dns.DnsResource;
|
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.DnsResponse;
|
||||||
import io.netty.handler.codec.dns.DnsResponseCode;
|
import io.netty.handler.codec.dns.DnsResponseCode;
|
||||||
import io.netty.handler.codec.dns.DnsType;
|
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.util.internal.ThreadLocalRandom;
|
import io.netty.util.internal.ThreadLocalRandom;
|
||||||
@ -181,7 +184,7 @@ public class DnsNameResolverTest {
|
|||||||
EXCLUSIONS_RESOLVE_A,
|
EXCLUSIONS_RESOLVE_A,
|
||||||
"akamaihd.net",
|
"akamaihd.net",
|
||||||
"googleusercontent.com",
|
"googleusercontent.com",
|
||||||
"");
|
StringUtil.EMPTY_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,7 +239,7 @@ public class DnsNameResolverTest {
|
|||||||
"people.com.cn",
|
"people.com.cn",
|
||||||
"googleusercontent.com",
|
"googleusercontent.com",
|
||||||
"blogspot.in",
|
"blogspot.in",
|
||||||
"");
|
StringUtil.EMPTY_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
||||||
@ -368,8 +371,8 @@ public class DnsNameResolverTest {
|
|||||||
public void testQueryMx() throws Exception {
|
public void testQueryMx() throws Exception {
|
||||||
assertThat(resolver.isRecursionDesired(), is(true));
|
assertThat(resolver.isRecursionDesired(), is(true));
|
||||||
|
|
||||||
Map<String, Future<DnsResponse>> futures =
|
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures =
|
||||||
new LinkedHashMap<String, Future<DnsResponse>>();
|
new LinkedHashMap<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>>();
|
||||||
for (String name: DOMAINS) {
|
for (String name: DOMAINS) {
|
||||||
if (EXCLUSIONS_QUERY_MX.contains(name)) {
|
if (EXCLUSIONS_QUERY_MX.contains(name)) {
|
||||||
continue;
|
continue;
|
||||||
@ -378,30 +381,36 @@ public class DnsNameResolverTest {
|
|||||||
queryMx(futures, name);
|
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();
|
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));
|
assertThat(response.code(), is(DnsResponseCode.NOERROR));
|
||||||
List<DnsResource> mxList = new ArrayList<DnsResource>();
|
|
||||||
for (DnsResource r: response.answers()) {
|
final int answerCount = response.count(DnsSection.ANSWER);
|
||||||
if (r.type() == DnsType.MX) {
|
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);
|
mxList.add(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(mxList.size(), is(greaterThan(0)));
|
assertThat(mxList.size(), is(greaterThan(0)));
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
for (DnsResource r: mxList) {
|
for (DnsRecord r: mxList) {
|
||||||
|
ByteBuf recordContent = ((ByteBufHolder) r).content();
|
||||||
|
|
||||||
buf.append(StringUtil.NEWLINE);
|
buf.append(StringUtil.NEWLINE);
|
||||||
buf.append('\t');
|
buf.append('\t');
|
||||||
buf.append(r.name());
|
buf.append(r.name());
|
||||||
buf.append(' ');
|
buf.append(' ');
|
||||||
buf.append(r.type());
|
buf.append(r.type().name());
|
||||||
buf.append(' ');
|
buf.append(' ');
|
||||||
buf.append(r.content().readUnsignedShort());
|
buf.append(recordContent.readUnsignedShort());
|
||||||
buf.append(' ');
|
buf.append(' ');
|
||||||
buf.append(DnsNameResolverContext.decodeDomainName(r.content()));
|
buf.append(DnsNameResolverContext.decodeDomainName(recordContent));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("{} has the following MX records:{}", hostname, buf);
|
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 unresolved =
|
||||||
InetSocketAddress.createUnresolved(hostname, ThreadLocalRandom.current().nextInt(65536));
|
InetSocketAddress.createUnresolved(hostname, ThreadLocalRandom.current().nextInt(65536));
|
||||||
|
|
||||||
futures.put(unresolved, resolver.resolve(unresolved));
|
futures.put(unresolved, resolver.resolve(unresolved));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void queryMx(Map<String, Future<DnsResponse>> futures, String hostname) throws Exception {
|
private static void queryMx(
|
||||||
futures.put(hostname, resolver.query(new DnsQuestion(hostname, DnsType.MX)));
|
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");
|
throw new NullPointerException("message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recipient == null && sender == null) {
|
||||||
|
throw new NullPointerException("recipient and sender");
|
||||||
|
}
|
||||||
|
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
|
Loading…
Reference in New Issue
Block a user