From 96e7d4489ab92ecc25bd5d0a30265b1e647e51ef Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Tue, 15 Mar 2016 13:55:20 +0100 Subject: [PATCH] Bugfixes in BinaryStruct and some Tests for MDNS --- src/zutil/ByteUtil.java | 2 +- .../{DNSPacket.java => DNSPacketHeader.java} | 156 +++++++----------- src/zutil/net/dns/DNSPacketQuestion.java | 69 ++++++++ src/zutil/net/dns/DNSPacketResource.java | 88 ++++++++++ src/zutil/net/dns/MulticastDNSClient.java | 29 ++-- src/zutil/parser/binary/BinaryFieldData.java | 9 +- .../binary/BinaryStructOutputStream.java | 2 +- test/zutil/net/dns/DNSPacketTest.java | 66 ++++++++ .../binary/BinaryStructOutputStreamTest.java | 16 +- 9 files changed, 320 insertions(+), 117 deletions(-) rename src/zutil/net/dns/{DNSPacket.java => DNSPacketHeader.java} (57%) create mode 100755 src/zutil/net/dns/DNSPacketQuestion.java create mode 100755 src/zutil/net/dns/DNSPacketResource.java create mode 100755 test/zutil/net/dns/DNSPacketTest.java diff --git a/src/zutil/ByteUtil.java b/src/zutil/ByteUtil.java index 2d106f4..f307b44 100755 --- a/src/zutil/ByteUtil.java +++ b/src/zutil/ByteUtil.java @@ -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) diff --git a/src/zutil/net/dns/DNSPacket.java b/src/zutil/net/dns/DNSPacketHeader.java similarity index 57% rename from src/zutil/net/dns/DNSPacket.java rename to src/zutil/net/dns/DNSPacketHeader.java index 2cef175..ad42efc 100755 --- a/src/zutil/net/dns/DNSPacket.java +++ b/src/zutil/net/dns/DNSPacketHeader.java @@ -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. *
@@ -64,21 +88,21 @@ public class DNSPacket implements BinaryStruct {
      * 
*/ @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 { * */ @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; + } } diff --git a/src/zutil/net/dns/DNSPacketQuestion.java b/src/zutil/net/dns/DNSPacketQuestion.java new file mode 100755 index 0000000..c9546fc --- /dev/null +++ b/src/zutil/net/dns/DNSPacketQuestion.java @@ -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. + */ + + + +} diff --git a/src/zutil/net/dns/DNSPacketResource.java b/src/zutil/net/dns/DNSPacketResource.java new file mode 100755 index 0000000..9b447d9 --- /dev/null +++ b/src/zutil/net/dns/DNSPacketResource.java @@ -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. + */ + +} diff --git a/src/zutil/net/dns/MulticastDNSClient.java b/src/zutil/net/dns/MulticastDNSClient.java index a803f55..db791a8 100755 --- a/src/zutil/net/dns/MulticastDNSClient.java +++ b/src/zutil/net/dns/MulticastDNSClient.java @@ -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()); } } diff --git a/src/zutil/parser/binary/BinaryFieldData.java b/src/zutil/parser/binary/BinaryFieldData.java index a367ea1..01df0df 100755 --- a/src/zutil/parser/binary/BinaryFieldData.java +++ b/src/zutil/parser/binary/BinaryFieldData.java @@ -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 { protected static List getStructFieldList(Class clazz){ if (!cache.containsKey(clazz)) { ArrayList 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 { } } - // 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(); } diff --git a/src/zutil/parser/binary/BinaryStructOutputStream.java b/src/zutil/parser/binary/BinaryStructOutputStream.java index 500cccc..9e6b1e0 100755 --- a/src/zutil/parser/binary/BinaryStructOutputStream.java +++ b/src/zutil/parser/binary/BinaryStructOutputStream.java @@ -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); diff --git a/test/zutil/net/dns/DNSPacketTest.java b/test/zutil/net/dns/DNSPacketTest.java new file mode 100755 index 0000000..b9e97f2 --- /dev/null +++ b/test/zutil/net/dns/DNSPacketTest.java @@ -0,0 +1,66 @@ +/* + * 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 org.junit.Test; +import zutil.parser.binary.BinaryStructOutputStream; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +/** + * Created by Ziver + */ +public class DNSPacketTest { + + @Test + public void headerQueryTest() throws IOException { + DNSPacketHeader header = new DNSPacketHeader(); + header.setDefaultQueryData(); + header.countQuestion = 1; + + byte[] data = BinaryStructOutputStream.serialize(header); + assertEquals("header length", 12, data.length); + assertEquals("Flag byte1", 0x00, data[2]); + assertEquals("Flag byte2", 0x00, data[3]); + assertEquals("Question count byte1", 0x00, data[4]); + assertEquals("Question count byte2", 0x01, data[5]); + } + + @Test + public void headerResponseTest() throws IOException { + DNSPacketHeader header = new DNSPacketHeader(); + header.setDefaultResponseData(); + header.countAnswerRecord = 1; + + byte[] data = BinaryStructOutputStream.serialize(header); + assertEquals("header length", 12, data.length); + assertEquals("Flag byte1", (byte)0x84, data[2]); + assertEquals("Flag byte2", (byte)0x00, data[3]); + assertEquals("Answer count byte1", 0x00, data[6]); + assertEquals("Answer count byte2", 0x01, data[7]); + } +} diff --git a/test/zutil/parser/binary/BinaryStructOutputStreamTest.java b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java index df50012..083cc60 100755 --- a/test/zutil/parser/binary/BinaryStructOutputStreamTest.java +++ b/test/zutil/parser/binary/BinaryStructOutputStreamTest.java @@ -42,7 +42,7 @@ public class BinaryStructOutputStreamTest { public void basicIntTest() throws IOException { BinaryStruct struct = new BinaryStruct() { @BinaryField(index=1, length=32) - public int i1 = 1; + int i1 = 1; @BinaryField(index=2, length=32) public int i2 = 2; }; @@ -51,6 +51,20 @@ public class BinaryStructOutputStreamTest { assertArrayEquals(new byte[]{0,0,0,1, 0,0,0,2}, data); } + @Test + public void shortIntTest() throws IOException { + BinaryStruct struct = new BinaryStruct() { + @BinaryField(index=1, length=16) + int i1 = 1; + @BinaryField(index=2, length=16) + int i2 = 2; + }; + + byte[] data = BinaryStructOutputStream.serialize(struct); + assertArrayEquals(new byte[]{0,1, 0,2}, data); + } + + @Test public void basicBooleanTest() throws IOException { BinaryStruct struct = new BinaryStruct() {