mirror of
https://github.com/deavmi/doap
synced 2024-07-07 06:10:07 +02:00
Packet
- Added `flip!(T)(T integral)` for byteswapping - Added `order!(T)(T integral, Order)` for byte swapping dependent on current system configuration - Added `CoapOption` for storing options - `CoapPacket`'s `options` field is now an array of `CoapOption` structs - `fromBytes(ubyte[])` now prints the partially decoded packet befor message ID and options processing - Added initial options support - Options are still not decoding all correctly - Added `OptionLenType` - Added `getOptionLenType(ubyte)` which determins the length type based on the 4 bits in the header (lower) - The `toString()` method now includes the `CoapOptions[]` in its output Packet (unit tests) - Added example CoAP packet to play with
This commit is contained in:
parent
d497494eeb
commit
2c65c61c0a
|
@ -10,6 +10,120 @@ import std.conv : to;
|
||||||
*/
|
*/
|
||||||
private ubyte PAYLOAD_MARKER = cast(ubyte)-1;
|
private ubyte PAYLOAD_MARKER = cast(ubyte)-1;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flips the given integral value
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* bytesIn = the integral value
|
||||||
|
* Returns: the flipped integral
|
||||||
|
*/
|
||||||
|
public T flip(T)(T bytesIn) if(__traits(isIntegral, T))
|
||||||
|
{
|
||||||
|
T copy = bytesIn;
|
||||||
|
|
||||||
|
ubyte* base = (cast(ubyte*)&bytesIn)+T.sizeof-1;
|
||||||
|
ubyte* baseCopy = cast(ubyte*)©
|
||||||
|
|
||||||
|
for(ulong idx = 0; idx < T.sizeof; idx++)
|
||||||
|
{
|
||||||
|
*(baseCopy+idx) = *(base-idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ordering
|
||||||
|
*/
|
||||||
|
private enum Order
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Little endian
|
||||||
|
*/
|
||||||
|
LE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Big endian
|
||||||
|
*/
|
||||||
|
BE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps the bytes to the given ordering but does a no-op
|
||||||
|
* if the ordering requested is the same as that of the
|
||||||
|
* system's
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* bytesIn = the integral value to swap
|
||||||
|
* order = the byte ordering to request
|
||||||
|
* Returns: the integral value
|
||||||
|
*/
|
||||||
|
public T order(T)(T bytesIn, Order order) if(__traits(isIntegral, T))
|
||||||
|
{
|
||||||
|
version(LittleEndian)
|
||||||
|
{
|
||||||
|
if(order == Order.LE)
|
||||||
|
{
|
||||||
|
return bytesIn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return flip(bytesIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else version(BigEndian)
|
||||||
|
{
|
||||||
|
if(order == Order.BE)
|
||||||
|
{
|
||||||
|
return bytesIn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return flip(bytesIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
version(LittleEndian)
|
||||||
|
{
|
||||||
|
ushort i = 1;
|
||||||
|
writeln("Pre-order: ", i);
|
||||||
|
ushort ordered = order(i, Order.BE);
|
||||||
|
writeln("Post-order: ", ordered);
|
||||||
|
assert(ordered == 256);
|
||||||
|
}
|
||||||
|
else version(BigEndian)
|
||||||
|
{
|
||||||
|
// TODO: Add this AND CI tests for it
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A header option
|
||||||
|
*/
|
||||||
|
public struct CoapOption
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Option ID
|
||||||
|
*/
|
||||||
|
public ushort id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option value
|
||||||
|
*/
|
||||||
|
public ubyte[] value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: remove this
|
||||||
|
import std.stdio : writeln;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a CoAP packet
|
* Represents a CoAP packet
|
||||||
*/
|
*/
|
||||||
|
@ -21,7 +135,7 @@ public class CoapPacket
|
||||||
private Code code;
|
private Code code;
|
||||||
private ushort mid;
|
private ushort mid;
|
||||||
private ubyte[] token;
|
private ubyte[] token;
|
||||||
private uint options;
|
private CoapOption[] options;
|
||||||
private ubyte[] payload;
|
private ubyte[] payload;
|
||||||
|
|
||||||
this()
|
this()
|
||||||
|
@ -172,6 +286,7 @@ public class CoapPacket
|
||||||
packet.tokenLen = data[0]&15;
|
packet.tokenLen = data[0]&15;
|
||||||
|
|
||||||
packet.code = cast(Code)(data[1]);
|
packet.code = cast(Code)(data[1]);
|
||||||
|
writeln("Decoded code: ", packet.code);
|
||||||
|
|
||||||
|
|
||||||
ubyte* midBase = data[2..4].ptr;
|
ubyte* midBase = data[2..4].ptr;
|
||||||
|
@ -193,10 +308,198 @@ public class CoapPacket
|
||||||
packet.token = data[4..4+packet.tokenLen];
|
packet.token = data[4..4+packet.tokenLen];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Do options decode here
|
||||||
|
ubyte[] remainder = data[4+packet.tokenLen..$];
|
||||||
|
version(unittest) writeln("Remainder: ", remainder);
|
||||||
|
|
||||||
|
ulong idx = 4+packet.tokenLen;
|
||||||
|
|
||||||
|
CoapOption[] createdOptions;
|
||||||
|
if(remainder.length)
|
||||||
|
{
|
||||||
|
// import std.container.slist : SList;
|
||||||
|
// SList!(CoapOption) createdOptions;
|
||||||
|
|
||||||
|
// First "previous" delta is 0
|
||||||
|
ushort delta = 0;
|
||||||
|
|
||||||
|
ushort curOptionNumber;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
writeln("Delta (ENTER): ", delta);
|
||||||
|
|
||||||
|
scope(exit)
|
||||||
|
{
|
||||||
|
writeln("Currently built options: ", createdOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte curValue = data[idx];
|
||||||
|
|
||||||
|
// If entire current value is -1/~0
|
||||||
|
// then we reached the payload marker
|
||||||
|
if(curValue == PAYLOAD_MARKER)
|
||||||
|
{
|
||||||
|
writeln("Found payload marker, stopping option parsing");
|
||||||
|
idx++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ubyte computed = (curValue&240) >> 4;
|
||||||
|
writeln("Computed delta: ", computed);
|
||||||
|
|
||||||
|
// 0 to 12 Option ID
|
||||||
|
if(computed >= 0 && computed <= 12)
|
||||||
|
{
|
||||||
|
writeln("Delta is 0 to 12");
|
||||||
|
|
||||||
|
// In such a case the delta we add on is this 4 bit eneity
|
||||||
|
delta+=computed;
|
||||||
|
writeln("Option id: ", delta);
|
||||||
|
|
||||||
|
|
||||||
|
// Get the type of option length
|
||||||
|
OptionLenType optLenType = getOptionLenType(curValue);
|
||||||
|
writeln("Option length type: ", optLenType);
|
||||||
|
|
||||||
|
// Simple case (12)
|
||||||
|
if(optLenType == OptionLenType.ZERO_TO_TWELVE)
|
||||||
|
{
|
||||||
|
// Compute the length
|
||||||
|
ubyte optLen = (curValue&15);
|
||||||
|
writeln("Option len: ", optLen);
|
||||||
|
|
||||||
|
// Update idx to jump over the (option delta | option length)
|
||||||
|
idx+=1;
|
||||||
|
|
||||||
|
// Grab the data from [idx, idx+length)
|
||||||
|
ubyte[] optionValue = data[idx..idx+optLen];
|
||||||
|
writeln("Option value: ", optionValue);
|
||||||
|
|
||||||
|
// Create the option and add it to the list of options
|
||||||
|
CoapOption option;
|
||||||
|
option.value = optionValue;
|
||||||
|
option.id = delta;
|
||||||
|
writeln("Built option: ", option);
|
||||||
|
createdOptions ~= option;
|
||||||
|
}
|
||||||
|
// Option length extended (8bit) (13)
|
||||||
|
else if(optLenType == OptionLenType._8BIT_EXTENDED)
|
||||||
|
{
|
||||||
|
// Next byte has the length
|
||||||
|
idx+=1;
|
||||||
|
|
||||||
|
// The total length is the extended value (which lacks 13 so we must add it)
|
||||||
|
writeln(data[idx..$]);
|
||||||
|
ubyte optLen8BitExt = data[idx];
|
||||||
|
ushort optLen = optLen8BitExt+13;
|
||||||
|
writeln("Option len: ", optLen);
|
||||||
|
|
||||||
|
// Jump over 8bit opt len ext
|
||||||
|
idx+=1;
|
||||||
|
|
||||||
|
// Grab the data from [idx, idx+optLen)
|
||||||
|
ubyte[] optionValue = data[idx..idx+optLen];
|
||||||
|
writeln("Option value: ", optionValue);
|
||||||
|
writeln("Option value: ", cast(string)optionValue);
|
||||||
|
|
||||||
|
// Jump over the option value
|
||||||
|
idx+=optLen;
|
||||||
|
|
||||||
|
// Create the option and add it to the list of options
|
||||||
|
CoapOption option;
|
||||||
|
option.value = optionValue;
|
||||||
|
option.id = delta;
|
||||||
|
writeln("Built option: ", option);
|
||||||
|
createdOptions ~= option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 13
|
||||||
|
else if(computed == 13)
|
||||||
|
{
|
||||||
|
writeln("3333 Option delta type: 13 - DEVELOPER ADD SUPPORT! 3333");
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
// 14
|
||||||
|
else if(computed == 14)
|
||||||
|
{
|
||||||
|
writeln("Option delta type: 14 - DEVELOPER ADD SUPPORT!");
|
||||||
|
|
||||||
|
// Skip over 4bit tuple
|
||||||
|
idx+=1;
|
||||||
|
|
||||||
|
// Delta value is 2 bytes (BE)
|
||||||
|
ubyte[] optionIdBytes = data[idx..idx+2];
|
||||||
|
ushort unProcessedValue = *(cast(ushort*)optionIdBytes.ptr);
|
||||||
|
ushort optionId = order(unProcessedValue, Order.BE);
|
||||||
|
|
||||||
|
|
||||||
|
writeln("16 bit option-id delta: ", optionId);
|
||||||
|
|
||||||
|
// Move onto the first byte of the next two (16 bit BE option-length extended)
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 15
|
||||||
|
else if(computed == 15)
|
||||||
|
{
|
||||||
|
writeln("FIVEFIVEFIVE Option delta type: 15 - DEVELOPER ADD SUPPORT! FIVEFIVEFIVE");
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packet.options = createdOptions;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum OptionLenType
|
||||||
|
{
|
||||||
|
ZERO_TO_TWELVE,
|
||||||
|
_8BIT_EXTENDED,
|
||||||
|
_12_BIT_EXTENDED,
|
||||||
|
UPPER_PAYLOAD_MARKER
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OptionLenType getOptionLenType(ubyte hdr)
|
||||||
|
{
|
||||||
|
ubyte type = (hdr&15);
|
||||||
|
if(type >= 0 && type <= 12)
|
||||||
|
{
|
||||||
|
return OptionLenType.ZERO_TO_TWELVE;
|
||||||
|
}
|
||||||
|
else if(type == 13)
|
||||||
|
{
|
||||||
|
return OptionLenType._8BIT_EXTENDED;
|
||||||
|
}
|
||||||
|
else if(type == 14)
|
||||||
|
{
|
||||||
|
return OptionLenType._12_BIT_EXTENDED;
|
||||||
|
}
|
||||||
|
else if(type == 15)
|
||||||
|
{
|
||||||
|
return OptionLenType.UPPER_PAYLOAD_MARKER;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeln("GetOptionLenType(): Major error if this happens");
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override string toString()
|
public override string toString()
|
||||||
{
|
{
|
||||||
return "CoapPacket [ver: "~to!(string)(ver)~
|
return "CoapPacket [ver: "~to!(string)(ver)~
|
||||||
|
@ -205,6 +508,7 @@ public class CoapPacket
|
||||||
", code: "~to!(string)(code)~
|
", code: "~to!(string)(code)~
|
||||||
", mid: "~to!(string)(mid)~
|
", mid: "~to!(string)(mid)~
|
||||||
", token: "~to!(string)(token)~
|
", token: "~to!(string)(token)~
|
||||||
|
", options: "~to!(string)(options)~
|
||||||
"]";
|
"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,3 +623,22 @@ unittest
|
||||||
// TODO: Add message ID check + token check
|
// TODO: Add message ID check + token check
|
||||||
assert(packet.getMessageId() == 257);
|
assert(packet.getMessageId() == 257);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
writeln("Begin big coap test (lekker real life)\n\n");
|
||||||
|
|
||||||
|
ubyte[] testingIn = [
|
||||||
|
0x41, 0x02, 0xcd, 0x47, 0x45, 0x3d,
|
||||||
|
0x03, 0x31, 0x30, 0x30, 0x2e, 0x36, 0x34, 0x2e,
|
||||||
|
0x30, 0x2e, 0x31, 0x32, 0x3a, 0x35,
|
||||||
|
0x36, 0x38, 0x33, 0x92, 0x27, 0x11, 0xe1, 0xfc,
|
||||||
|
0xd0, 0x01, 0x21, 0x10, 0x21, 0x01,
|
||||||
|
0xff, 0xc0, 0x01, 0xc1, 0x00, 0x0f, 0x00, 0x00,
|
||||||
|
0x28, 0x00, 0x00, 0xff, 0x02, 0x00
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
CoapPacket packet = CoapPacket.fromBytes(testingIn);
|
||||||
|
writeln(packet);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user