Cleaned up header, moved time to text.

This commit is contained in:
Felix Ableitner 2014-12-12 03:13:37 +02:00
parent 5cbf918d86
commit bd9ea26bd5
7 changed files with 43 additions and 53 deletions

View file

@ -37,7 +37,7 @@ the AES key is wrapped with the recipient's public RSA key.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ / / /
\ Header (variable length) \ \ Header (76 bytes) \
/ / / /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ / / /
@ -52,19 +52,17 @@ the AES key is wrapped with the recipient's public RSA key.
### Header ### Header
Every message starts with one 32 bit word indicating the message Every message starts with one 76 byte header indicating the message
version, type and ID, followed by the length of the message. The version, type and ID, followed by the length of the message. The
header is in network byte order, i.e. big endian. header is in network byte order, i.e. big endian.
0 1 2 3 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ver | Type | Hop Limit | Hop Count | | Version | Type | Hop Limit | Hop Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | |
| Origin Address | | Origin Address |
| | | |
@ -73,15 +71,15 @@ header is in network byte order, i.e. big endian.
| Target Address | | Target Address |
| | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number | Metric | Reserved | | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Body Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Ver specifies the protocol version number. This is currently 0. A Version specifies the protocol version number. This is currently 0. A
message with unknown version number MUST be ignored. The connection message with unknown version number MUST be ignored. The connection
where such a packet came from MAY be closed. where such a packet came from MAY be closed.
Type is one of the message types specified below.
Hop Limit SHOULD be set to `MAX_HOP_COUNT` on message creation, and Hop Limit SHOULD be set to `MAX_HOP_COUNT` on message creation, and
MUST NOT be changed by a forwarding node. MUST NOT be changed by a forwarding node.
@ -102,9 +100,6 @@ message.
Target Address is the address of the node that should receive the Target Address is the address of the node that should receive the
message. message.
Sequence number is the sequence number of either the source or target
node for this message, depending on type.
### Encryption Data ### Encryption Data
@ -203,7 +198,7 @@ A simple chat message.
0 1 2 3 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved | | Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Text Length | | Text Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -212,4 +207,6 @@ A simple chat message.
/ / / /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Time is the unix timestamp of message sending.
Text the string to be transferred, encoded as UTF-8. Text the string to be transferred, encoded as UTF-8.

View file

@ -10,19 +10,15 @@ import com.nutomic.ensichat.protocol.messages.MessageHeaderTest._
object MessageHeaderTest { object MessageHeaderTest {
val h1 = new MessageHeader(Text.Type, MessageHeader.DefaultHopLimit, AddressTest.a1, val h1 = new MessageHeader(Text.Type, MessageHeader.DefaultHopLimit, AddressTest.a1,
AddressTest.a2, 1234, 0, new GregorianCalendar(1970, 1, 1).getTime, 567, 8) AddressTest.a2, 1234, 0)
val h2 = new MessageHeader(Text.Type, 0, AddressTest.a1, AddressTest.a3, 8765, 234, val h2 = new MessageHeader(Text.Type, 0, AddressTest.a1, AddressTest.a3, 8765, 234)
new GregorianCalendar(2014, 6, 10).getTime, 0, 0xff)
val h3 = new MessageHeader(Text.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56, val h3 = new MessageHeader(Text.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56)
new GregorianCalendar(2020, 11, 11).getTime, 0xffff, 0)
val h4 = new MessageHeader(0xfff, 0, Address.Null, Address.Broadcast, 0, 0xff, val h4 = new MessageHeader(0xfff, 0, Address.Null, Address.Broadcast, 0, 0xff)
new GregorianCalendar(1990, 1, 1).getTime, 0, 0xff)
val h5 = new MessageHeader(ConnectionInfo.Type, 0xff, Address.Broadcast, Address.Null, 0xffff, 0, val h5 = new MessageHeader(ConnectionInfo.Type, 0xff, Address.Broadcast, Address.Null, 0xffff, 0)
new GregorianCalendar(2035, 12, 31).getTime, 0xffff, 0)
val headers = Set(h1, h2, h3, h4, h5) val headers = Set(h1, h2, h3, h4, h5)

View file

@ -13,11 +13,11 @@ import scala.collection.immutable.TreeSet
object MessageTest { object MessageTest {
val m1 = new Message(h1, new Text("first")) val m1 = new Message(h1, new Text("first", new GregorianCalendar(1970, 1, 1).getTime))
val m2 = new Message(h2, new Text("second")) val m2 = new Message(h2, new Text("second", new GregorianCalendar(2014, 6, 10).getTime))
val m3 = new Message(h3, new Text("third")) val m3 = new Message(h3, new Text("third", new GregorianCalendar(2020, 11, 11).getTime))
val messages = Set(m1, m2, m3) val messages = Set(m1, m2, m3)
@ -47,8 +47,7 @@ class MessageTest extends AndroidTestCase {
} }
def testSerializeSigned(): Unit = { def testSerializeSigned(): Unit = {
val header = new MessageHeader(ConnectionInfo.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56, val header = new MessageHeader(ConnectionInfo.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56)
new GregorianCalendar(2020, 11, 11).getTime, 0xffff, 0)
val m = new Message(header, ConnectionInfoTest.generateCi(getContext)) val m = new Message(header, ConnectionInfoTest.generateCi(getContext))
val signed = Crypto.sign(m) val signed = Crypto.sign(m)

View file

@ -8,7 +8,10 @@ object Message {
* Orders messages by date, oldest messages first. * Orders messages by date, oldest messages first.
*/ */
val Ordering = new Ordering[Message] { val Ordering = new Ordering[Message] {
override def compare(m1: Message, m2: Message) = m1.Header.Time.compareTo(m2.Header.Time) override def compare(m1: Message, m2: Message) = (m1.Body, m2.Body) match {
case (t1: Text, t2: Text) => t1.time.compareTo(t2.time)
case _ => 0
}
} }
def read(stream: InputStream): Message = { def read(stream: InputStream): Message = {

View file

@ -7,7 +7,7 @@ import com.nutomic.ensichat.protocol.{Address, BufferUtils}
object MessageHeader { object MessageHeader {
val Length = 20 + 2 * Address.Length val Length = 12 + 2 * Address.Length
val DefaultHopLimit = 20 val DefaultHopLimit = 20
@ -31,14 +31,10 @@ object MessageHeader {
val hopCount = BufferUtils.getUnsignedByte(b) val hopCount = BufferUtils.getUnsignedByte(b)
val length = BufferUtils.getUnsignedInt(b) val length = BufferUtils.getUnsignedInt(b)
val time = new Date(b.getInt().toLong * 1000)
val origin = new Address(BufferUtils.getByteArray(b, Address.Length)) val origin = new Address(BufferUtils.getByteArray(b, Address.Length))
val target = new Address(BufferUtils.getByteArray(b, Address.Length)) val target = new Address(BufferUtils.getByteArray(b, Address.Length))
val seqNum = BufferUtils.getUnsignedShort(b) new MessageHeader(messageType, hopLimit, origin, target, length, hopCount)
val metric = BufferUtils.getUnsignedByte(b)
new MessageHeader(messageType, hopLimit, origin, target, seqNum, metric, time, length, hopCount)
} }
} }
@ -50,9 +46,6 @@ case class MessageHeader(MessageType: Int,
HopLimit: Int, HopLimit: Int,
Origin: Address, Origin: Address,
Target: Address, Target: Address,
SequenceNumber: Int,
Metric: Int,
Time: Date = new Date(),
Length: Long = -1, Length: Long = -1,
HopCount: Int = 0) { HopCount: Int = 0) {
@ -68,13 +61,10 @@ case class MessageHeader(MessageType: Int,
BufferUtils.putUnsignedByte(b, HopCount) BufferUtils.putUnsignedByte(b, HopCount)
BufferUtils.putUnsignedInt(b, MessageHeader.Length + contentLength) BufferUtils.putUnsignedInt(b, MessageHeader.Length + contentLength)
b.putInt((Time.getTime / 1000).toInt)
b.put(Origin.Bytes) b.put(Origin.Bytes)
b.put(Target.Bytes) b.put(Target.Bytes)
BufferUtils.putUnsignedShort(b, SequenceNumber) BufferUtils.putUnsignedInt(b, 0)
BufferUtils.putUnsignedByte(b, Metric)
BufferUtils.putUnsignedByte(b, 0)
b.array() b.array()
} }
@ -83,11 +73,8 @@ case class MessageHeader(MessageType: Int,
case o: MessageHeader => case o: MessageHeader =>
MessageType == o.MessageType && MessageType == o.MessageType &&
HopLimit == o.HopLimit && HopLimit == o.HopLimit &&
Time.getTime / 1000 == o.Time.getTime / 1000 &&
Origin == o.Origin && Origin == o.Origin &&
Target == o.Target && Target == o.Target &&
SequenceNumber == o.SequenceNumber &&
Metric == o.Metric &&
HopCount == o.HopCount HopCount == o.HopCount
// Don't compare length as it may be unknown (when header was just created without a body). // Don't compare length as it may be unknown (when header was just created without a body).
case _ => false case _ => false

View file

@ -1,6 +1,7 @@
package com.nutomic.ensichat.protocol.messages package com.nutomic.ensichat.protocol.messages
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.Date
import com.nutomic.ensichat.protocol.BufferUtils import com.nutomic.ensichat.protocol.BufferUtils
@ -15,10 +16,11 @@ object Text {
*/ */
def read(array: Array[Byte]): Text = { def read(array: Array[Byte]): Text = {
val b = ByteBuffer.wrap(array) val b = ByteBuffer.wrap(array)
val time = new Date(BufferUtils.getUnsignedInt(b) * 1000)
val length = BufferUtils.getUnsignedInt(b).toInt val length = BufferUtils.getUnsignedInt(b).toInt
val bytes = new Array[Byte](length) val bytes = new Array[Byte](length)
b.get(bytes, 0, length) b.get(bytes, 0, length)
new Text(new String(bytes, Text.Charset)) new Text(new String(bytes, Text.Charset), time)
} }
} }
@ -26,18 +28,24 @@ object Text {
/** /**
* Holds a plain text message. * Holds a plain text message.
*/ */
case class Text(text: String) extends MessageBody { case class Text(text: String, time: Date = new Date()) extends MessageBody {
override def Type = Text.Type override def Type = Text.Type
override def write: Array[Byte] = { override def write: Array[Byte] = {
val bytes = text.getBytes(Text.Charset) val bytes = text.getBytes(Text.Charset)
val b = ByteBuffer.allocate(4 + bytes.length) val b = ByteBuffer.allocate(length)
BufferUtils.putUnsignedInt(b, time.getTime / 1000)
BufferUtils.putUnsignedInt(b, bytes.length) BufferUtils.putUnsignedInt(b, bytes.length)
b.put(bytes) b.put(bytes)
b.array() b.array()
} }
override def length = write.length override def length = 8 + text.getBytes(Text.Charset).length
override def equals(a: Any): Boolean = a match {
case o: Text => text == text && time.getTime / 1000 == o.time.getTime / 1000
case _ => false
}
} }

View file

@ -58,9 +58,9 @@ class Database(context: Context) extends SQLiteOpenHelper(context, Database.Data
new Address(c.getString(c.getColumnIndex("origin"))), new Address(c.getString(c.getColumnIndex("origin"))),
new Address(c.getString(c.getColumnIndex("target"))), new Address(c.getString(c.getColumnIndex("target"))),
-1, -1,
-1, -1)
val body = new Text(new String(c.getString(c.getColumnIndex ("text"))),
new Date(c.getLong(c.getColumnIndex("date")))) new Date(c.getLong(c.getColumnIndex("date"))))
val body = new Text(new String(c.getString(c.getColumnIndex ("text"))))
messages += new Message(header, body) messages += new Message(header, body)
} }
c.close() c.close()
@ -71,13 +71,13 @@ class Database(context: Context) extends SQLiteOpenHelper(context, Database.Data
* Inserts the given new message into the database. * Inserts the given new message into the database.
*/ */
def addMessage(message: Message): Unit = message.Body match { def addMessage(message: Message): Unit = message.Body match {
case msg: Text => case text: Text =>
val cv = new ContentValues() val cv = new ContentValues()
cv.put("origin", message.Header.Origin.toString) cv.put("origin", message.Header.Origin.toString)
cv.put("target", message.Header.Target.toString) cv.put("target", message.Header.Target.toString)
// toString used as workaround for compile error with Long. // toString used as workaround for compile error with Long.
cv.put("date", message.Header.Time.getTime.toString) cv.put("date", text.time.getTime.toString)
cv.put("text", msg.text) cv.put("text", text.text)
getWritableDatabase.insert("messages", null, cv) getWritableDatabase.insert("messages", null, cv)
case _: ConnectionInfo | _: RequestAddContact | _: ResultAddContact => case _: ConnectionInfo | _: RequestAddContact | _: ResultAddContact =>
// Never stored. // Never stored.