125 lines
4.6 KiB
Java
125 lines
4.6 KiB
Java
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
}
|