[#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:
Norman Maurer 2016-03-24 11:20:54 +01:00
parent 9a183ec38f
commit 6bf7e24389
4 changed files with 103 additions and 2 deletions

View File

@ -107,7 +107,19 @@ public class DefaultDnsRecordDecoder implements DnsRecordDecoder {
int position = -1;
int checked = 0;
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()) {
boolean pointer = (len & 0xc0) == 0xc0;
if (pointer) {

View File

@ -80,10 +80,14 @@ public class DefaultDnsRecordEncoder implements DnsRecordEncoder {
String[] parts = StringUtil.split(name, '.');
for (String part: parts) {
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) {
continue;
}
buf.writeByte(partLen);
ByteBufUtil.writeAscii(buf, part);
}
buf.writeByte(0); // marks end of name field

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}