[#5014] Correctly encode / decode zero-length names when encoding DnsRecords.
Motivation: Zero-length names needs to be "prefixed" by the length as well when encoded into a ByteBuf. Also some servers not correctly prefix these so we should ensure we can workaround this and even decode in such case. Modifications: - Always encode the length of the name into the ByteBuf even if its zero-length. - If there are no readable bytes for the name just asume its an empty name to workaround dns servers that not fully respect the RFC. Result: Correctly encode zero-length names and be able to decode empty names even when the rfc is not strictly followed.
This commit is contained in:
parent
9a183ec38f
commit
6bf7e24389
@ -107,7 +107,19 @@ public class DefaultDnsRecordDecoder implements DnsRecordDecoder {
|
|||||||
int position = -1;
|
int position = -1;
|
||||||
int checked = 0;
|
int checked = 0;
|
||||||
final int end = in.writerIndex();
|
final int end = in.writerIndex();
|
||||||
final StringBuilder name = new StringBuilder(in.readableBytes() << 1);
|
final int readable = in.readableBytes();
|
||||||
|
|
||||||
|
// Looking at the spec we should always have at least enough readable bytes to read a byte here but it seems
|
||||||
|
// some servers do not respect this for empty names. So just workaround this and return an empty name in this
|
||||||
|
// case.
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// - https://github.com/netty/netty/issues/5014
|
||||||
|
// - https://www.ietf.org/rfc/rfc1035.txt , Section 3.1
|
||||||
|
if (readable == 0) {
|
||||||
|
return StringUtil.EMPTY_STRING;
|
||||||
|
}
|
||||||
|
final StringBuilder name = new StringBuilder(readable << 1);
|
||||||
for (int len = in.readUnsignedByte(); in.isReadable() && len != 0; len = in.readUnsignedByte()) {
|
for (int len = in.readUnsignedByte(); in.isReadable() && len != 0; len = in.readUnsignedByte()) {
|
||||||
boolean pointer = (len & 0xc0) == 0xc0;
|
boolean pointer = (len & 0xc0) == 0xc0;
|
||||||
if (pointer) {
|
if (pointer) {
|
||||||
|
@ -80,10 +80,14 @@ public class DefaultDnsRecordEncoder implements DnsRecordEncoder {
|
|||||||
String[] parts = StringUtil.split(name, '.');
|
String[] parts = StringUtil.split(name, '.');
|
||||||
for (String part: parts) {
|
for (String part: parts) {
|
||||||
final int partLen = part.length();
|
final int partLen = part.length();
|
||||||
|
// We always need to write the length even if its 0.
|
||||||
|
// See:
|
||||||
|
// - https://github.com/netty/netty/issues/5014
|
||||||
|
// - https://www.ietf.org/rfc/rfc1035.txt , Section 3.1
|
||||||
|
buf.writeByte(partLen);
|
||||||
if (partLen == 0) {
|
if (partLen == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
buf.writeByte(partLen);
|
|
||||||
ByteBufUtil.writeAscii(buf, part);
|
ByteBufUtil.writeAscii(buf, part);
|
||||||
}
|
}
|
||||||
buf.writeByte(0); // marks end of name field
|
buf.writeByte(0); // marks end of name field
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.Unpooled;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DefaultDnsRecordDecoderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodeEmptyName() {
|
||||||
|
testDecodeEmptyName0(Unpooled.buffer().writeByte('0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodeEmptyNameNonRFC() {
|
||||||
|
testDecodeEmptyName0(Unpooled.EMPTY_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testDecodeEmptyName0(ByteBuf buffer) {
|
||||||
|
try {
|
||||||
|
DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder();
|
||||||
|
Assert.assertEquals(StringUtil.EMPTY_STRING, decoder.decodeName(buffer));
|
||||||
|
} finally {
|
||||||
|
buffer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.Unpooled;
|
||||||
|
import io.netty.util.internal.StringUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class DefaultDnsRecordEncoderTest {
|
||||||
|
|
||||||
|
// Test for https://github.com/netty/netty/issues/5014
|
||||||
|
@Test
|
||||||
|
public void testEncodeEmptyName() throws Exception {
|
||||||
|
DefaultDnsRecordEncoder encoder = new DefaultDnsRecordEncoder();
|
||||||
|
ByteBuf out = Unpooled.buffer();
|
||||||
|
try {
|
||||||
|
encoder.encodeName(StringUtil.EMPTY_STRING, out);
|
||||||
|
assertEquals(2, out.readableBytes());
|
||||||
|
assertEquals(0, out.readByte());
|
||||||
|
assertEquals(0, out.readByte());
|
||||||
|
} finally {
|
||||||
|
out.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user