Fixed DNS FQDN parsing
This commit is contained in:
parent
f55473ced0
commit
600a4b648f
9 changed files with 86 additions and 64 deletions
|
|
@ -16,7 +16,7 @@ public class PositionalInputStream extends FilterInputStream {
|
|||
/**
|
||||
* @param in the underlying input stream.
|
||||
*/
|
||||
protected PositionalInputStream(InputStream in) {
|
||||
public PositionalInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
|
|
@ -60,8 +60,8 @@ public class PositionalInputStream extends FilterInputStream {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readlimit) {
|
||||
super.mark(readlimit);
|
||||
public void mark(int readLimit) {
|
||||
super.mark(readLimit);
|
||||
|
||||
synchronized(this) {
|
||||
mark = pos;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ public class MulticastDnsClient extends ThreadedUDPNetwork implements ThreadedUD
|
|||
int id = 0; // Needs to be zero when doing multicast
|
||||
activeProbes.add(id);
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
BinaryStructOutputStream out = new BinaryStructOutputStream(buffer);
|
||||
|
||||
DnsPacket dnsPacket = new DnsPacket();
|
||||
dnsPacket.getHeader().id = id;
|
||||
|
|
@ -87,7 +86,7 @@ public class MulticastDnsClient extends ThreadedUDPNetwork implements ThreadedUD
|
|||
domain,
|
||||
DnsConstants.TYPE.SRV,
|
||||
DnsConstants.CLASS.IN));
|
||||
dnsPacket.write(out);
|
||||
dnsPacket.write(buffer);
|
||||
|
||||
DatagramPacket udpPacket = new DatagramPacket(
|
||||
buffer.toByteArray(), buffer.size(),
|
||||
|
|
@ -106,8 +105,7 @@ public class MulticastDnsClient extends ThreadedUDPNetwork implements ThreadedUD
|
|||
try {
|
||||
ByteArrayInputStream buffer = new ByteArrayInputStream(packet.getData(),
|
||||
packet.getOffset(), packet.getLength());
|
||||
BinaryStructInputStream in = new BinaryStructInputStream(buffer);
|
||||
DnsPacket dnsPacket = DnsPacket.read(in);
|
||||
DnsPacket dnsPacket = DnsPacket.read(buffer);
|
||||
|
||||
//System.out.println("Received:\n" +ByteUtil.toFormattedString(packet.getData(), packet.getOffset(), packet.getLength()));
|
||||
MultiPrintStream.out.dump(dnsPacket,3);
|
||||
|
|
|
|||
|
|
@ -112,8 +112,7 @@ public class MulticastDnsServer extends ThreadedUDPNetwork implements ThreadedUD
|
|||
try {
|
||||
ByteArrayInputStream buffer = new ByteArrayInputStream(packet.getData(),
|
||||
packet.getOffset(), packet.getLength());
|
||||
BinaryStructInputStream in = new BinaryStructInputStream(buffer);
|
||||
DnsPacket dnsPacket = DnsPacket.read(in);
|
||||
DnsPacket dnsPacket = DnsPacket.read(buffer);
|
||||
|
||||
// Just handle queries and no responses
|
||||
if (! dnsPacket.getHeader().flagQueryResponse) {
|
||||
|
|
@ -121,9 +120,8 @@ public class MulticastDnsServer extends ThreadedUDPNetwork implements ThreadedUD
|
|||
|
||||
if (response != null) {
|
||||
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
|
||||
BinaryStructOutputStream out = new BinaryStructOutputStream(outBuffer);
|
||||
response.write(out);
|
||||
out.close();
|
||||
response.write(outBuffer);
|
||||
outBuffer.close();
|
||||
|
||||
DatagramPacket outPacket = new DatagramPacket(
|
||||
outBuffer.toByteArray(), outBuffer.size(),
|
||||
|
|
|
|||
|
|
@ -24,10 +24,13 @@
|
|||
|
||||
package zutil.net.dns.packet;
|
||||
|
||||
import zutil.io.PositionalInputStream;
|
||||
import zutil.parser.binary.BinaryStructInputStream;
|
||||
import zutil.parser.binary.BinaryStructOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -94,7 +97,9 @@ public class DnsPacket {
|
|||
}
|
||||
|
||||
|
||||
public static DnsPacket read(BinaryStructInputStream structIn) throws IOException {
|
||||
public static DnsPacket read(InputStream in) throws IOException {
|
||||
BinaryStructInputStream structIn = new BinaryStructInputStream(new PositionalInputStream(in));
|
||||
|
||||
DnsPacket packet = new DnsPacket();
|
||||
structIn.read(packet.header);
|
||||
|
||||
|
|
@ -116,7 +121,9 @@ public class DnsPacket {
|
|||
}
|
||||
}
|
||||
|
||||
public void write(BinaryStructOutputStream structOut) throws IOException {
|
||||
public void write(OutputStream out) throws IOException {
|
||||
BinaryStructOutputStream structOut = new BinaryStructOutputStream(out);
|
||||
|
||||
structOut.write(header);
|
||||
|
||||
for (DnsPacketQuestion question : questions)
|
||||
|
|
|
|||
|
|
@ -24,40 +24,64 @@
|
|||
|
||||
package zutil.net.dns.packet;
|
||||
|
||||
import zutil.io.PositionalInputStream;
|
||||
import zutil.parser.binary.BinaryFieldData;
|
||||
import zutil.parser.binary.BinaryFieldSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A serializer class that can read and write a DNS FQDN in binary format.
|
||||
*/
|
||||
public class FQDNStringSerializer implements BinaryFieldSerializer<String> {
|
||||
private HashMap<Integer, String> stringCache = new HashMap<>();
|
||||
|
||||
|
||||
public String read(InputStream in, BinaryFieldData field) throws IOException {
|
||||
StringBuilder str = new StringBuilder();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
int pos = (int) ((PositionalInputStream) in).getPosition();
|
||||
int c;
|
||||
|
||||
while ((c=in.read()) > 0) {
|
||||
if (str.length() > 0) // Don't add dot to first loop
|
||||
str.append('.');
|
||||
if (buffer.length() > 0) // Don't add dot to first loop
|
||||
buffer.append('.');
|
||||
|
||||
if ((c & 0b1100_0000) == 0b1100_0000) {
|
||||
// This a offset pointer to the String data
|
||||
// This is an offset pointer to the String data
|
||||
int offset = (c & 0b0011_1111) << 8;
|
||||
offset |= in.read() & 0b1111_1111;
|
||||
str.append(offset);
|
||||
|
||||
if (stringCache.containsKey(offset))
|
||||
buffer.append(stringCache.get(offset));
|
||||
else
|
||||
buffer.append('<').append(offset).append('>');
|
||||
break; // PTR is always the last part of the FQDN
|
||||
} else {
|
||||
// Normal String data
|
||||
// Read normal String data
|
||||
for (int i = 0; i < c; ++i) {
|
||||
str.append((char) in.read());
|
||||
buffer.append((char) in.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
|
||||
String output = buffer.toString();
|
||||
|
||||
// Populate cache
|
||||
if (in instanceof PositionalInputStream) {
|
||||
stringCache.put(pos, output);
|
||||
for (int index = 0; index >= 0;) {
|
||||
index = buffer.indexOf(".", index);
|
||||
if (index >= 0) {
|
||||
++index;
|
||||
stringCache.put(pos + index, buffer.substring(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public void write(OutputStream out, String domain, BinaryFieldData field) throws IOException {
|
||||
|
|
@ -70,5 +94,4 @@ public class FQDNStringSerializer implements BinaryFieldSerializer<String> {
|
|||
}
|
||||
out.write(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,15 +29,13 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An Interface where custom field parser and writer can be implemented.
|
||||
* An Interface defining a custom field parser and writer.
|
||||
* <p></p>
|
||||
* One instance of the serializer and will have the scope of the methods
|
||||
* {@link BinaryStructInputStream#read(BinaryStruct)} and {@link BinaryStructOutputStream#write(BinaryStruct)}
|
||||
* where as it will be deallocated after the methods have returned.
|
||||
* One singleton instance of the serializer will be instantiated for the lifetime of the
|
||||
* {@link BinaryStructInputStream} and {@link BinaryStructOutputStream} objects.
|
||||
* <p></p>
|
||||
* NOTE: Partial octet serializing not supported.
|
||||
*
|
||||
* Created by Ziver on 2016-04-11.
|
||||
*/
|
||||
public interface BinaryFieldSerializer<T> {
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import java.util.Map;
|
|||
|
||||
/**
|
||||
* A stream class that parses a byte stream into binary struct objects.
|
||||
* <p><p/>
|
||||
* <p></p>
|
||||
* Limitations:<br>
|
||||
* - Does not support sub binary objects.<br>
|
||||
*
|
||||
|
|
@ -47,6 +47,9 @@ public class BinaryStructInputStream {
|
|||
private byte data;
|
||||
private int dataBitIndex = -1;
|
||||
|
||||
private Map<Class, BinaryFieldSerializer> serializerCache = new HashMap<>();
|
||||
|
||||
|
||||
public BinaryStructInputStream(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
|
@ -78,7 +81,6 @@ public class BinaryStructInputStream {
|
|||
*/
|
||||
public int read(BinaryStruct struct) throws IOException {
|
||||
List<BinaryFieldData> structDataList = BinaryFieldData.getStructFieldList(struct.getClass());
|
||||
Map<Class, BinaryFieldSerializer> serializerCache = new HashMap<>();
|
||||
|
||||
int totalReadLength = 0;
|
||||
for (BinaryFieldData field : structDataList) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import java.util.Map;
|
|||
|
||||
/**
|
||||
* A stream class that generates a byte stream from a binary struct objects.
|
||||
* <p><p/>
|
||||
* <p></p>
|
||||
* Limitations:<br>
|
||||
* - Does not support sub binary objects.<br>
|
||||
*
|
||||
|
|
@ -48,6 +48,8 @@ public class BinaryStructOutputStream {
|
|||
private byte rest;
|
||||
private int restBitLength; // length from Most Significant Bit
|
||||
|
||||
private Map<Class, BinaryFieldSerializer> serializerCache = new HashMap<>();
|
||||
|
||||
|
||||
public BinaryStructOutputStream(OutputStream out) {
|
||||
this.out = out;
|
||||
|
|
@ -88,7 +90,6 @@ public class BinaryStructOutputStream {
|
|||
*/
|
||||
public void write(BinaryStruct struct) throws IOException {
|
||||
List<BinaryFieldData> structDataList = BinaryFieldData.getStructFieldList(struct.getClass());
|
||||
Map<Class, BinaryFieldSerializer> serializerCache = new HashMap<>();
|
||||
|
||||
for (BinaryFieldData field : structDataList) {
|
||||
if (field.hasSerializer()) {
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ public class DnsPacketTest {
|
|||
packet.getHeader().setDefaultQueryData();
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
BinaryStructOutputStream out = new BinaryStructOutputStream(buffer);
|
||||
packet.write(out);
|
||||
packet.write(buffer);
|
||||
byte[] data = buffer.toByteArray();
|
||||
|
||||
byte[] expected = {
|
||||
|
|
@ -97,8 +96,7 @@ public class DnsPacketTest {
|
|||
"appletv.local", DnsConstants.TYPE.A, DnsConstants.CLASS.IN));
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
BinaryStructOutputStream out = new BinaryStructOutputStream(buffer);
|
||||
packet.write(out);
|
||||
packet.write(buffer);
|
||||
byte[] data = buffer.toByteArray();
|
||||
|
||||
byte[] expected = {
|
||||
|
|
@ -155,8 +153,7 @@ public class DnsPacketTest {
|
|||
0xc0, 0x0c, 0x00, 0x04, 0x40, 0x00, 0x00, 0x08 // NSEC
|
||||
};
|
||||
ByteArrayInputStream buffer = new ByteArrayInputStream(Converter.toBytes(input));
|
||||
BinaryStructInputStream in = new BinaryStructInputStream(buffer);
|
||||
DnsPacket packet = DnsPacket.read(in);
|
||||
DnsPacket packet = DnsPacket.read(buffer);
|
||||
|
||||
// Assert Header
|
||||
assertTrue("flagQueryResponse", packet.getHeader().flagQueryResponse);
|
||||
|
|
@ -217,10 +214,9 @@ Domain Name System (query)
|
|||
question.clazz = DnsConstants.CLASS.IN;
|
||||
packet.addQuestion(question);
|
||||
|
||||
ByteArrayOutputStream buff = new ByteArrayOutputStream();
|
||||
BinaryStructOutputStream out = new BinaryStructOutputStream(buff);
|
||||
packet.write(out);
|
||||
byte[] data = buff.toByteArray();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
packet.write(buffer);
|
||||
byte[] data = buffer.toByteArray();
|
||||
assertEquals((
|
||||
"241a 01 00 00 01 00 00 00 00 00 00 " +
|
||||
"03 77 77 77 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 0001 0001"
|
||||
|
|
@ -297,8 +293,7 @@ Domain Name System (response)
|
|||
"c02c 0001 0001 000000e3 0004 42 f9 59 68" // Answer3
|
||||
).replace(" ", ""));
|
||||
ByteArrayInputStream buffer = new ByteArrayInputStream(input);
|
||||
BinaryStructInputStream in = new BinaryStructInputStream(buffer);
|
||||
DnsPacket packet = DnsPacket.read(in);
|
||||
DnsPacket packet = DnsPacket.read(buffer);
|
||||
|
||||
assertEquals("id", 0x241a, packet.getHeader().id);
|
||||
assertTrue("flagQueryResponse", packet.getHeader().flagQueryResponse);
|
||||
|
|
@ -315,19 +310,20 @@ Domain Name System (response)
|
|||
|
||||
// Answer
|
||||
DnsPacketResource answer = packet.getAnswerRecords().get(0);
|
||||
assertEquals("NAME", "12", answer.name);
|
||||
assertEquals("NAME", "www.google.com", answer.name);
|
||||
//assertEquals("NAME", "<12>", answer.name);
|
||||
assertEquals("TYPE", DnsConstants.TYPE.CNAME, answer.type);
|
||||
assertEquals("CLASS", DnsConstants.CLASS.IN, answer.clazz);
|
||||
assertEquals("TTL", 337977, answer.ttl);
|
||||
|
||||
answer = packet.getAnswerRecords().get(1);
|
||||
assertEquals("NAME", "44", answer.name);
|
||||
assertEquals("NAME", "<44>", answer.name);
|
||||
assertEquals("TYPE", DnsConstants.TYPE.A, answer.type);
|
||||
assertEquals("CLASS", DnsConstants.CLASS.IN, answer.clazz);
|
||||
assertEquals("TTL", 227, answer.ttl);
|
||||
|
||||
answer = packet.getAnswerRecords().get(2);
|
||||
assertEquals("NAME", "44", answer.name);
|
||||
assertEquals("NAME", "<44>", answer.name);
|
||||
assertEquals("TYPE", DnsConstants.TYPE.A, answer.type);
|
||||
assertEquals("CLASS", DnsConstants.CLASS.IN, answer.clazz);
|
||||
assertEquals("TTL", 227, answer.ttl);
|
||||
|
|
@ -415,17 +411,16 @@ Multicast Domain Name System (query)
|
|||
|
||||
byte[] input = Converter.hexToByte((
|
||||
"00 00 00 00 00 06 00 00 00 00 00 01" + // Header
|
||||
"0f 5f 63 6f 6d 70 61 6e 69 6f 6e 2d 6c 69 6e 6b 04 5f 74 63 70 05 6c 6f 63 61 6c 00 00 0c 00 01" + // Query PTR: _companion-link._tcp.local
|
||||
"08 5f 68 6f 6d 65 6b 69 74 c0 1c 00 0c 00 01" + // Query PTR: _homekit._tcp.local
|
||||
"03 68 61 6c c0 21 00 41 00 01" + // Query Unknown: hal.local
|
||||
"c0 3b 00 1c 00 01" + // Query IPv6: hal.local
|
||||
"c0 3b 00 01 00 01" + // Query IPv4: hal.local
|
||||
"0f 5f 63 6f 6d 70 61 6e 69 6f 6e 2d 6c 69 6e 6b 04 5f 74 63 70 05 6c 6f 63 61 6c 00 00 0c 00 01" + // (offset 12) Query PTR: _companion-link._tcp.local
|
||||
"08 5f 68 6f 6d 65 6b 69 74 c0 1c 00 0c 00 01" + // (offset 44) Query PTR: _homekit._tcp.local
|
||||
"03 68 61 6c c0 21 00 41 00 01" + // (offset 59) Query Unknown: hal.local
|
||||
"c0 3b 00 1c 00 01" + // (offset 69) Query IPv6: hal.local
|
||||
"c0 3b 00 01 00 01" + // (offset 75) Query IPv4: hal.local
|
||||
"0c 5f 73 6c 65 65 70 2d 70 72 6f 78 79 04 5f 75 64 70 c0 21 00 0c 00 01" + // Query PTR: _sleep-proxy._udp.local
|
||||
"00 00 29 05 a0 00 00 11 94 00 12 00 04 00 0e 00 65 7a e6 ba 29 34 00 d6 07 3a f2 2e e7" // Additional records
|
||||
).replace(" ", ""));
|
||||
ByteArrayInputStream buffer = new ByteArrayInputStream(input);
|
||||
BinaryStructInputStream in = new BinaryStructInputStream(buffer);
|
||||
DnsPacket packet = DnsPacket.read(in);
|
||||
DnsPacket packet = DnsPacket.read(buffer);
|
||||
|
||||
assertEquals("id", 0x00, packet.getHeader().id);
|
||||
assertFalse("flagQueryResponse", packet.getHeader().flagQueryResponse);
|
||||
|
|
@ -441,31 +436,31 @@ Multicast Domain Name System (query)
|
|||
assertEquals("clazz", DnsConstants.CLASS.IN, question1.clazz);
|
||||
|
||||
DnsPacketQuestion question2 = packet.getQuestions().get(1);
|
||||
//assertEquals("qNAME", "_homekit._tcp.local", question2.name);
|
||||
assertEquals("qNAME", "_homekit.28", question2.name); // TODO: Fix support for string pointers
|
||||
assertEquals("qNAME", "_homekit._tcp.local", question2.name);
|
||||
//assertEquals("qNAME", "_homekit.<28>", question2.name); // TODO: Fix support for string pointers
|
||||
assertEquals("type", DnsConstants.TYPE.PTR, question2.type);
|
||||
assertEquals("clazz", DnsConstants.CLASS.IN, question2.clazz);
|
||||
|
||||
DnsPacketQuestion question3 = packet.getQuestions().get(2);
|
||||
//assertEquals("qNAME", "hal.local", question3.name);
|
||||
assertEquals("qNAME", "hal.33", question3.name);
|
||||
assertEquals("qNAME", "hal.local", question3.name);
|
||||
//assertEquals("qNAME", "hal.<33>", question3.name);
|
||||
assertEquals("clazz", DnsConstants.CLASS.IN, question3.clazz);
|
||||
|
||||
DnsPacketQuestion question4 = packet.getQuestions().get(3);
|
||||
//assertEquals("qNAME", "hal.local", question4.name);
|
||||
assertEquals("qNAME", "59", question4.name);
|
||||
assertEquals("qNAME", "hal.local", question4.name);
|
||||
//assertEquals("qNAME", "<59>", question4.name);
|
||||
assertEquals("type", DnsConstants.TYPE.AAAA, question4.type);
|
||||
assertEquals("clazz", DnsConstants.CLASS.IN, question4.clazz);
|
||||
|
||||
DnsPacketQuestion question5 = packet.getQuestions().get(4);
|
||||
//assertEquals("qNAME", "hal.local", question5.name);
|
||||
assertEquals("qNAME", "59", question5.name);
|
||||
assertEquals("qNAME", "hal.local", question5.name);
|
||||
//assertEquals("qNAME", "<59>", question5.name);
|
||||
assertEquals("type", DnsConstants.TYPE.A, question5.type);
|
||||
assertEquals("clazz", DnsConstants.CLASS.IN, question5.clazz);
|
||||
|
||||
DnsPacketQuestion question6 = packet.getQuestions().get(5);
|
||||
//assertEquals("qNAME", "_sleep-proxy._udp.local", question6.name);
|
||||
assertEquals("qNAME", "_sleep-proxy._udp.33", question6.name);
|
||||
assertEquals("qNAME", "_sleep-proxy._udp.local", question6.name);
|
||||
//assertEquals("qNAME", "_sleep-proxy._udp.<33>", question6.name);
|
||||
assertEquals("type", DnsConstants.TYPE.PTR, question6.type);
|
||||
assertEquals("clazz", DnsConstants.CLASS.IN, question6.clazz);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue