Added DNS packet TC
This commit is contained in:
parent
fffaa64455
commit
31a47f52cd
5 changed files with 232 additions and 20 deletions
|
|
@ -1,17 +1,19 @@
|
||||||
package zutil.net.dns;
|
package zutil.net.dns;
|
||||||
|
|
||||||
|
import zutil.parser.binary.BinaryStructInputStream;
|
||||||
import zutil.parser.binary.BinaryStructOutputStream;
|
import zutil.parser.binary.BinaryStructOutputStream;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a general wrapper for a whole DNS packet.
|
* This class is a general wrapper for a whole DNS packet.
|
||||||
*
|
*
|
||||||
* Created by ezivkoc on 2016-04-11.
|
* Created by Ziver on 2016-04-11.
|
||||||
*/
|
*/
|
||||||
public class DNSPacket {
|
public class DNSPacket {
|
||||||
private DNSPacketHeader header;
|
private DNSPacketHeader header;
|
||||||
|
|
@ -30,23 +32,77 @@ public class DNSPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static DNSPacket read(InputStream in){
|
public DNSPacketHeader getHeader(){
|
||||||
return null;
|
return header;
|
||||||
|
}
|
||||||
|
public List<DNSPacketQuestion> getQuestions(){
|
||||||
|
return Collections.unmodifiableList(questions);
|
||||||
|
}
|
||||||
|
public List<DNSPacketResource> getAnswerRecords(){
|
||||||
|
return Collections.unmodifiableList(answerRecords);
|
||||||
|
}
|
||||||
|
public List<DNSPacketResource> getNameServers(){
|
||||||
|
return Collections.unmodifiableList(nameServers);
|
||||||
|
}
|
||||||
|
public List<DNSPacketResource> getAdditionalRecords(){
|
||||||
|
return Collections.unmodifiableList(additionalRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addQuestion(DNSPacketQuestion question){
|
||||||
|
questions.add(question);
|
||||||
|
header.countQuestion = questions.size();
|
||||||
|
}
|
||||||
|
public void addAnswerRecord(DNSPacketResource resource){
|
||||||
|
answerRecords.add(resource);
|
||||||
|
header.countAnswerRecord = answerRecords.size();
|
||||||
|
}
|
||||||
|
public void addNameServer(DNSPacketResource resource){
|
||||||
|
nameServers.add(resource);
|
||||||
|
header.countNameServer = nameServers.size();
|
||||||
|
}
|
||||||
|
public void addAdditionalRecord(DNSPacketResource resource){
|
||||||
|
additionalRecords.add(resource);
|
||||||
|
header.countAdditionalRecord = additionalRecords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static DNSPacket read(InputStream in) throws IOException {
|
||||||
|
BinaryStructInputStream structIn = new BinaryStructInputStream(in);
|
||||||
|
DNSPacket packet = new DNSPacket();
|
||||||
|
structIn.read(packet.header);
|
||||||
|
|
||||||
|
for (int i=0; i<packet.header.countQuestion; ++i) {
|
||||||
|
DNSPacketQuestion question = new DNSPacketQuestion();
|
||||||
|
structIn.read(question);
|
||||||
|
packet.questions.add(question);
|
||||||
|
}
|
||||||
|
readResource(structIn, packet.header.countAnswerRecord, packet.answerRecords);
|
||||||
|
readResource(structIn, packet.header.countNameServer, packet.nameServers);
|
||||||
|
readResource(structIn, packet.header.countAdditionalRecord, packet.additionalRecords);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
private static void readResource(BinaryStructInputStream structIn, int count, ArrayList<DNSPacketResource> list) throws IOException {
|
||||||
|
for (int i=0; i<count; ++i){
|
||||||
|
DNSPacketResource resource = new DNSPacketResource();
|
||||||
|
structIn.read(resource);
|
||||||
|
list.add(resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(OutputStream out) throws IOException {
|
public void write(OutputStream out) throws IOException {
|
||||||
BinaryStructOutputStream structOut = new BinaryStructOutputStream(out);
|
BinaryStructOutputStream structOut = new BinaryStructOutputStream(out);
|
||||||
structOut.write(header);
|
structOut.write(header);
|
||||||
out.flush();
|
|
||||||
|
|
||||||
/*for (DNSPacketQuestion question : questions)
|
for (DNSPacketQuestion question : questions)
|
||||||
question.write(out);
|
structOut.write(question);
|
||||||
for (DNSPacketResource answerRecord : answerRecords)
|
for (DNSPacketResource answerRecord : answerRecords)
|
||||||
answerRecord.write(out);
|
structOut.write(answerRecord);
|
||||||
for (DNSPacketResource nameServer : nameServers)
|
for (DNSPacketResource nameServer : nameServers)
|
||||||
nameServer.write(out);
|
structOut.write(nameServer);
|
||||||
for (DNSPacketResource additionalRecord : additionalRecords)
|
for (DNSPacketResource additionalRecord : additionalRecords)
|
||||||
additionalRecord.write(out);*/
|
structOut.write(additionalRecord);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,57 @@ import java.io.OutputStream;
|
||||||
* Reference: http://tools.ietf.org/html/rfc1035
|
* Reference: http://tools.ietf.org/html/rfc1035
|
||||||
*/
|
*/
|
||||||
public class DNSPacketQuestion implements BinaryStruct {
|
public class DNSPacketQuestion implements BinaryStruct {
|
||||||
|
/** a host address */
|
||||||
|
public static final int QTYPE_A = 1;
|
||||||
|
/** an authoritative name server */
|
||||||
|
public static final int QTYPE_NS = 2;
|
||||||
|
/** a mail destination (Obsolete - use MX) */
|
||||||
|
public static final int QTYPE_MD = 3;
|
||||||
|
/** a mail forwarder (Obsolete - use MX) */
|
||||||
|
public static final int QTYPE_MF = 4;
|
||||||
|
/** the canonical name for an alias */
|
||||||
|
public static final int QTYPE_CNAME = 5;
|
||||||
|
/** marks the start of a zone of authority */
|
||||||
|
public static final int QTYPE_SOA = 6;
|
||||||
|
/** a mailbox domain name (EXPERIMENTAL) */
|
||||||
|
public static final int QTYPE_MB = 7;
|
||||||
|
/** a mail group member (EXPERIMENTAL) */
|
||||||
|
public static final int QTYPE_MG = 8;
|
||||||
|
/** a mail rename domain name (EXPERIMENTAL) */
|
||||||
|
public static final int QTYPE_MR = 9;
|
||||||
|
/** a null RR (EXPERIMENTAL) */
|
||||||
|
public static final int QTYPE_NULL = 10;
|
||||||
|
/** a well known service description */
|
||||||
|
public static final int QTYPE_WKS = 11;
|
||||||
|
/** a domain name pointer */
|
||||||
|
public static final int QTYPE_PTR = 12;
|
||||||
|
/** host information */
|
||||||
|
public static final int QTYPE_HINFO = 13;
|
||||||
|
/** mailbox or mail list information */
|
||||||
|
public static final int QTYPE_MINFO = 14;
|
||||||
|
/** mail exchange */
|
||||||
|
public static final int QTYPE_MX = 15;
|
||||||
|
/** text strings */
|
||||||
|
public static final int QTYPE_TXT = 16;
|
||||||
|
/** A request for a transfer of an entire zone */
|
||||||
|
public static final int QTYPE_AXFR = 252;
|
||||||
|
/** A request for mailbox-related records (MB, MG or MR) */
|
||||||
|
public static final int QTYPE_MAILB = 253;
|
||||||
|
/** A request for mail agent RRs (Obsolete - see MX) */
|
||||||
|
public static final int QTYPE_MAILA = 254;
|
||||||
|
/** A request for all records */
|
||||||
|
public static final int QTYPE_ANY = 255;
|
||||||
|
|
||||||
|
/** the Internet */
|
||||||
|
public static final int QCLASS_IN = 1;
|
||||||
|
/** the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
|
||||||
|
public static final int QCLASS_CS = 2;
|
||||||
|
/** the CHAOS class */
|
||||||
|
public static final int QCLASS_CH = 3;
|
||||||
|
/** Hesiod [Dyer 87] */
|
||||||
|
public static final int QCLASS_HS = 4;
|
||||||
|
/** any class */
|
||||||
|
public static final int QCLASS_ANY = 255;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Question section format
|
Question section format
|
||||||
|
|
@ -64,7 +115,7 @@ public class DNSPacketQuestion implements BinaryStruct {
|
||||||
* padding is used.
|
* padding is used.
|
||||||
*/
|
*/
|
||||||
@CustomBinaryField(index=10, serializer=DomainStringSerializer.class)
|
@CustomBinaryField(index=10, serializer=DomainStringSerializer.class)
|
||||||
private String name;
|
private String qName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a two octet code which specifies the type of the query.
|
* a two octet code which specifies the type of the query.
|
||||||
|
|
@ -72,15 +123,27 @@ public class DNSPacketQuestion implements BinaryStruct {
|
||||||
* TYPE field, together with some more general codes which
|
* TYPE field, together with some more general codes which
|
||||||
* can match more than one type of RR.
|
* can match more than one type of RR.
|
||||||
*/
|
*/
|
||||||
@BinaryField(index=10, length=1)
|
@BinaryField(index=10, length=16)
|
||||||
private int type;
|
private int qType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a two octet code that specifies the class of the query.
|
* a two octet code that specifies the class of the query.
|
||||||
* For example, the QCLASS field is IN for the Internet.
|
* For example, the QCLASS field is IN for the Internet.
|
||||||
*/
|
*/
|
||||||
@BinaryField(index=20, length=1)
|
@BinaryField(index=20, length=16)
|
||||||
private int clazz;
|
private int qClass;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public DNSPacketQuestion() {}
|
||||||
|
public DNSPacketQuestion(String qName, int qType, int qClass) {
|
||||||
|
this.qName = qName;
|
||||||
|
this.qType = qType;
|
||||||
|
this.qClass = qClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class DomainStringSerializer implements BinaryFieldSerializer<String> {
|
public static class DomainStringSerializer implements BinaryFieldSerializer<String> {
|
||||||
|
|
@ -101,7 +164,7 @@ public class DNSPacketQuestion implements BinaryStruct {
|
||||||
|
|
||||||
public void write(OutputStream out, String domain, BinaryFieldData field) throws IOException {
|
public void write(OutputStream out, String domain, BinaryFieldData field) throws IOException {
|
||||||
if (domain != null){
|
if (domain != null){
|
||||||
String[] labels = domain.split(".");
|
String[] labels = domain.split("\\.");
|
||||||
for (String label : labels) {
|
for (String label : labels) {
|
||||||
out.write(label.length());
|
out.write(label.length());
|
||||||
out.write(label.getBytes());
|
out.write(label.getBytes());
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,51 @@ import zutil.parser.binary.BinaryStruct;
|
||||||
*/
|
*/
|
||||||
public class DNSPacketResource implements BinaryStruct {
|
public class DNSPacketResource implements BinaryStruct {
|
||||||
|
|
||||||
|
/** a host address */
|
||||||
|
public static final int TYPE_A = 1;
|
||||||
|
/** an authoritative name server */
|
||||||
|
public static final int TYPE_NS = 2;
|
||||||
|
/** a mail destination (Obsolete - use MX) */
|
||||||
|
public static final int TYPE_MD = 3;
|
||||||
|
/** a mail forwarder (Obsolete - use MX) */
|
||||||
|
public static final int TYPE_MF = 4;
|
||||||
|
/** the canonical name for an alias */
|
||||||
|
public static final int TYPE_CNAME = 5;
|
||||||
|
/** marks the start of a zone of authority */
|
||||||
|
public static final int TYPE_SOA = 6;
|
||||||
|
/** a mailbox domain name (EXPERIMENTAL) */
|
||||||
|
public static final int TYPE_MB = 7;
|
||||||
|
/** a mail group member (EXPERIMENTAL) */
|
||||||
|
public static final int TYPE_MG = 8;
|
||||||
|
/** a mail rename domain name (EXPERIMENTAL) */
|
||||||
|
public static final int TYPE_MR = 9;
|
||||||
|
/** a null RR (EXPERIMENTAL) */
|
||||||
|
public static final int TYPE_NULL = 10;
|
||||||
|
/** a well known service description */
|
||||||
|
public static final int TYPE_WKS = 11;
|
||||||
|
/** a domain name pointer */
|
||||||
|
public static final int TYPE_PTR = 12;
|
||||||
|
/** host information */
|
||||||
|
public static final int TYPE_HINFO = 13;
|
||||||
|
/** mailbox or mail list information */
|
||||||
|
public static final int TYPE_MINFO = 14;
|
||||||
|
/** mail exchange */
|
||||||
|
public static final int TYPE_MX = 15;
|
||||||
|
/** text strings */
|
||||||
|
public static final int TYPE_TXT = 16;
|
||||||
|
|
||||||
|
/** the Internet */
|
||||||
|
public static final int CLASS_IN = 1;
|
||||||
|
/** the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
|
||||||
|
public static final int CLASS_CS = 2;
|
||||||
|
/** the CHAOS class */
|
||||||
|
public static final int CLASS_CH = 3;
|
||||||
|
/** Hesiod [Dyer 87] */
|
||||||
|
public static final int CLASS_HS = 4;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The answer, authority, and additional sections all share the same
|
The answer, authority, and additional sections all share the same
|
||||||
format: a variable number of resource records, where the number of
|
format: a variable number of resource records, where the number of
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ public class BinaryStructOutputStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
localFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,12 @@ package zutil.net.dns;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import zutil.parser.binary.BinaryStructOutputStream;
|
import zutil.parser.binary.BinaryStructOutputStream;
|
||||||
|
import static zutil.net.dns.DNSPacketQuestion.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,7 +40,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
public class DNSPacketTest {
|
public class DNSPacketTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headerQueryTest() throws IOException {
|
public void writeHeaderTest() throws IOException {
|
||||||
DNSPacketHeader header = new DNSPacketHeader();
|
DNSPacketHeader header = new DNSPacketHeader();
|
||||||
header.setDefaultQueryData();
|
header.setDefaultQueryData();
|
||||||
header.countQuestion = 1;
|
header.countQuestion = 1;
|
||||||
|
|
@ -51,16 +54,60 @@ public class DNSPacketTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headerResponseTest() throws IOException {
|
public void readHheaderTest() throws IOException {
|
||||||
DNSPacketHeader header = new DNSPacketHeader();
|
DNSPacketHeader header = new DNSPacketHeader();
|
||||||
header.setDefaultResponseData();
|
header.setDefaultResponseData();
|
||||||
header.countAnswerRecord = 1;
|
header.countAnswerRecord = 1;
|
||||||
|
|
||||||
byte[] data = BinaryStructOutputStream.serialize(header);
|
byte[] data = BinaryStructOutputStream.serialize(header);
|
||||||
assertEquals("header length", 12, data.length);
|
assertEquals("header length", 12, data.length);
|
||||||
assertEquals("Flag byte1", (byte)0x84, data[2]);
|
assertEquals("Flag byte1", (byte) 0x84, data[2]);
|
||||||
assertEquals("Flag byte2", (byte)0x00, data[3]);
|
assertEquals("Flag byte2", (byte) 0x00, data[3]);
|
||||||
assertEquals("Answer count byte1", 0x00, data[6]);
|
assertEquals("Answer count byte1", 0x00, data[6]);
|
||||||
assertEquals("Answer count byte2", 0x01, data[7]);
|
assertEquals("Answer count byte2", 0x01, data[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeDnsPacketHeaderTest() throws IOException {
|
||||||
|
DNSPacket packet = new DNSPacket();
|
||||||
|
packet.getHeader().setDefaultQueryData();
|
||||||
|
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
packet.write(buffer);
|
||||||
|
byte[] data = buffer.toByteArray();
|
||||||
|
|
||||||
|
byte[] expected = {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, // QDCOUNT
|
||||||
|
0x00, 0x00, // ANCOUNT
|
||||||
|
0x00, 0x00, // NSCOUNT
|
||||||
|
0x00, 0x00, // ARCOUNT
|
||||||
|
};
|
||||||
|
assertArrayEquals(expected, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeDnsPacketTest() throws IOException {
|
||||||
|
DNSPacket packet = new DNSPacket();
|
||||||
|
packet.getHeader().setDefaultQueryData();
|
||||||
|
packet.addQuestion(new DNSPacketQuestion("appletv.local", QTYPE_A, QCLASS_IN));
|
||||||
|
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
packet.write(buffer);
|
||||||
|
byte[] data = buffer.toByteArray();
|
||||||
|
|
||||||
|
byte[] expected = {
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, // QDCOUNT
|
||||||
|
0x00, 0x00, // ANCOUNT
|
||||||
|
0x00, 0x00, // NSCOUNT
|
||||||
|
0x00, 0x00, // ARCOUNT
|
||||||
|
0x07, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x74, 0x76, // "apple"
|
||||||
|
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // "local"
|
||||||
|
0x00, // NULL
|
||||||
|
0x00, 0x01, // QTYPE
|
||||||
|
0x00, 0x01 // QCLASS
|
||||||
|
};
|
||||||
|
assertArrayEquals(expected, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue