Bugfixes in BinaryStruct and some Tests for MDNS

This commit is contained in:
Ziver Koc 2016-03-15 13:55:20 +01:00
parent 64a9b4126c
commit 96e7d4489a
9 changed files with 320 additions and 117 deletions

View file

@ -65,7 +65,6 @@ public class ByteUtil {
* @return a new byte containing a sub byte defined by the index and length
*/
public static byte getBits(byte data, int index, int length){
length--;
byte ret = (byte) (data & getBitMask(index, length));
return ret;
}
@ -77,6 +76,7 @@ public class ByteUtil {
* @param length length of mask from index, valid values 1-8
*/
public static byte getBitMask(int index, int length) {
--length;
if(0 > index || index > 7)
throw new IllegalArgumentException("Invalid index argument, allowed value is 0-7");
if(length < 0 && index-length < 0)

View file

@ -30,7 +30,7 @@ import zutil.parser.binary.BinaryStruct;
* Created by Ziver on 2016-02-09.
* Reference: http://tools.ietf.org/html/rfc1035
*/
public class DNSPacket implements BinaryStruct {
public class DNSPacketHeader implements BinaryStruct {
public static final int OPCODE_QUERY = 0;
public static final int OPCODE_IQUERY = 1;
public static final int OPCODE_STATUS = 2;
@ -42,9 +42,33 @@ public class DNSPacket implements BinaryStruct {
public static final int RCODE_NOT_IMPLEMENTED = 4;
public static final int RCODE_REFUSED = 5;
/*
Header section format
The header contains the following fields:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
*/
@BinaryField(index=0, length=16)
int id;
public int id;
//////////////// FLAGS
//@BinaryField(index=10, length=16)
@ -54,7 +78,7 @@ public class DNSPacket implements BinaryStruct {
* query (0), or a response (1).
*/
@BinaryField(index=10, length=1)
boolean flagQueryResponse;
public boolean flagQueryResponse;
/**
* A four bit field that specifies kind of query in this message.
* <pre>
@ -64,21 +88,21 @@ public class DNSPacket implements BinaryStruct {
* </pre>
*/
@BinaryField(index=11, length=4)
int flagOperationCode;
public int flagOperationCode;
/**
* This bit is valid in responses,
* and specifies that the responding name server is an
* authority for the domain name in question section.
*/
@BinaryField(index=12, length=1)
boolean flagAuthoritativeAnswer;
public boolean flagAuthoritativeAnswer;
/**
* specifies that this message was truncated
* due to length greater than that permitted on the
* transmission channel.
*/
@BinaryField(index=13, length=1)
boolean flagTruncation;
public boolean flagTruncation;
/**
* this bit may be set in a query and
* is copied into the response. If RD is set, it directs
@ -86,20 +110,20 @@ public class DNSPacket implements BinaryStruct {
* Recursive query support is optional.
*/
@BinaryField(index=14, length=1)
boolean flagRecursionDesired;
public boolean flagRecursionDesired;
/**
* this be is set or cleared in a
* response, and denotes whether recursive query support is
* available in the name server.
*/
@BinaryField(index=15, length=1)
boolean flagRecursionAvailable;
public boolean flagRecursionAvailable;
/**
* Reserved for future use. Must be zero in all queries
* and responses.
*/
@BinaryField(index=16, length=3)
private int z;
protected int z;
/**
* this field is set as part of responses.
* The values have the following interpretation:
@ -123,7 +147,7 @@ public class DNSPacket implements BinaryStruct {
*</pre>
*/
@BinaryField(index=17, length=4)
int flagResponseCode;
public int flagResponseCode;
//////////////// COUNTS
@ -132,115 +156,57 @@ public class DNSPacket implements BinaryStruct {
* Specifying the number of entries in the question section.
*/
@BinaryField(index=20, length=16)
int countQuestion;
public int countQuestion;
/**
* Answer Record Count.
* specifying the number of resource records in
* the answer section.
*/
@BinaryField(index=21, length=16)
int countAnswerRecord;
public int countAnswerRecord;
/**
* Name Server (Authority Record) Count.
* Specifying the number of name server resource records
* in the authority records section.
*/
@BinaryField(index=22, length=16)
int countNameServer;
public int countNameServer;
/**
* Additional Record Count.
* Specifying the number of resource records in the
* additional records section
*/
@BinaryField(index=23, length=16)
int countAdditionalRecord;
public int countAdditionalRecord;
/*
Question section format
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
QNAME a domain name represented as a sequence of labels, where
each label consists of a length octet followed by that
number of octets. The domain name terminates with the
zero length octet for the null label of the root. Note
that this field may be an odd number of octets; no
padding is used.
QTYPE a two octet code which specifies the type of the query.
The values for this field include all codes valid for a
TYPE field, together with some more general codes which
can match more than one type of RR.
public void setDefaultQueryData() {
// Set all flags to zero
flagQueryResponse = false;
flagOperationCode = 0;
flagAuthoritativeAnswer = false;
flagTruncation = false;
flagRecursionDesired = false;
flagRecursionAvailable = false;
z = 0;
flagResponseCode = 0;
}
QCLASS a two octet code that specifies the class of the query.
For example, the QCLASS field is IN for the Internet.
*/
public void setDefaultResponseData() {
flagQueryResponse = true;
flagAuthoritativeAnswer = true;
/*
Resource record format
The answer, authority, and additional sections all share the same
format: a variable number of resource records, where the number of
records is specified in the corresponding count field in the header.
Each resource record has the following format:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
NAME a domain name to which this resource record pertains.
TYPE two octets containing one of the RR type codes. This
field specifies the meaning of the data in the RDATA
field.
CLASS two octets which specify the class of the data in the
RDATA field.
TTL a 32 bit unsigned integer that specifies the time
interval (in seconds) that the resource record may be
cached before it should be discarded. Zero values are
interpreted to mean that the RR can only be used for the
transaction in progress, and should not be cached.
RDLENGTH an unsigned 16 bit integer that specifies the length in
octets of the RDATA field.
RDATA a variable length string of octets that describes the
resource. The format of this information varies
according to the TYPE and CLASS of the resource record.
For example, the if the TYPE is A and the CLASS is IN,
the RDATA field is a 4 octet ARPA Internet address.
*/
// Set the rest to zero
// TODO: all flags should not be zeroed
flagOperationCode = 0;
flagTruncation = false;
flagRecursionDesired = false;
flagRecursionAvailable = false;
z = 0;
flagResponseCode = 0;
}
}

View file

@ -0,0 +1,69 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package zutil.net.dns;
import zutil.parser.binary.BinaryStruct;
/**
* Created by Ziver on 2016-02-09.
* Reference: http://tools.ietf.org/html/rfc1035
*/
public class DNSPacketQuestion implements BinaryStruct {
/*
Question section format
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ QNAME /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
QNAME a domain name represented as a sequence of labels, where
each label consists of a length octet followed by that
number of octets. The domain name terminates with the
zero length octet for the null label of the root. Note
that this field may be an odd number of octets; no
padding is used.
QTYPE a two octet code which specifies the type of the query.
The values for this field include all codes valid for a
TYPE field, together with some more general codes which
can match more than one type of RR.
QCLASS a two octet code that specifies the class of the query.
For example, the QCLASS field is IN for the Internet.
*/
}

View file

@ -0,0 +1,88 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package zutil.net.dns;
import zutil.parser.binary.BinaryStruct;
/**
* Created by Ziver on 2016-02-09.
* Reference: http://tools.ietf.org/html/rfc1035
*/
public class DNSPacketResource implements BinaryStruct {
/*
The answer, authority, and additional sections all share the same
format: a variable number of resource records, where the number of
records is specified in the corresponding count field in the header.
Each resource record has the following format:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
where:
NAME a domain name to which this resource record pertains.
TYPE two octets containing one of the RR type codes. This
field specifies the meaning of the data in the RDATA
field.
CLASS two octets which specify the class of the data in the
RDATA field.
TTL a 32 bit unsigned integer that specifies the time
interval (in seconds) that the resource record may be
cached before it should be discarded. Zero values are
interpreted to mean that the RR can only be used for the
transaction in progress, and should not be cached.
RDLENGTH an unsigned 16 bit integer that specifies the length in
octets of the RDATA field.
RDATA a variable length string of octets that describes the
resource. The format of this information varies
according to the TYPE and CLASS of the resource record.
For example, the if the TYPE is A and the CLASS is IN,
the RDATA field is a 4 octet ARPA Internet address.
*/
}

View file

@ -33,7 +33,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Created by Ziver
@ -49,29 +48,25 @@ public class MulticastDNSClient extends ThreadedUDPNetwork implements ThreadedUD
}
public void sendProbe() {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
BinaryStructOutputStream out = new BinaryStructOutputStream(buffer);
public void sendProbe() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
BinaryStructOutputStream out = new BinaryStructOutputStream(buffer);
DNSPacket header = new DNSPacket();
out.write(header);
DNSPacketHeader header = new DNSPacketHeader();
out.write(header);
DatagramPacket packet = null;
DatagramPacket packet = null;
packet = new DatagramPacket(
buffer.toByteArray(), buffer.size(),
InetAddress.getByName( MDNS_MULTICAST_ADDR ),
MDNS_MULTICAST_PORT );
send(packet);
} catch (Exception e) {
e.printStackTrace();
}
packet = new DatagramPacket(
buffer.toByteArray(), buffer.size(),
InetAddress.getByName( MDNS_MULTICAST_ADDR ),
MDNS_MULTICAST_PORT );
send(packet);
}
@Override
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
DNSPacket header = new DNSPacket();
DNSPacketHeader header = new DNSPacketHeader();
BinaryStructInputStream.read(header, packet.getData());
}
}

View file

@ -1,12 +1,15 @@
package zutil.parser.binary;
import java.io.InvalidClassException;
import java.io.InvalidObjectException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import zutil.ByteUtil;
import zutil.converter.Converter;
import zutil.parser.binary.BinaryStruct.BinaryField;
@ -24,7 +27,7 @@ public class BinaryFieldData implements Comparable<BinaryFieldData> {
protected static List<BinaryFieldData> getStructFieldList(Class<? extends BinaryStruct> clazz){
if (!cache.containsKey(clazz)) {
ArrayList<BinaryFieldData> list = new ArrayList<>();
for (Field field : clazz.getFields()) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(BinaryField.class))
list.add(new BinaryFieldData(field));
}
@ -56,15 +59,17 @@ public class BinaryFieldData implements Comparable<BinaryFieldData> {
}
}
// TODO: variable length support
protected byte[] getValue(Object obj){
try {
field.setAccessible(true);
if (field.getType() == Boolean.class || field.getType() == boolean.class)
return new byte[]{ (byte)(field.getBoolean(obj) ? 0x01 : 0x00) };
else if (field.getType() == Integer.class || field.getType() == int.class)
return Converter.toBytes(field.getInt(obj));
else if (field.getType() == String.class)
return ((String)(field.get(obj))).getBytes();
else
throw new UnsupportedOperationException("Unsupported BinaryStruct field class: "+ field.getClass());
} catch (IllegalAccessException e){
e.printStackTrace();
}

View file

@ -76,7 +76,7 @@ public class BinaryStructOutputStream {
byte[] data = field.getValue(struct);
int fieldBitLength = field.getBitLength();
for (int i=data.length-1; fieldBitLength>0; fieldBitLength-=8, --i) {
for (int i=(int)Math.ceil(fieldBitLength/8.0)-1; fieldBitLength>0; fieldBitLength-=8, --i) {
byte b = data[i];
if (restBitLength == 0 && fieldBitLength >= 8)
out.write(0xFF & b);