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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ Header (variable length) \
\ Header (76 bytes) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
@ -52,19 +52,17 @@ the AES key is wrapped with the recipient's public RSA key.
### 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
header is in network byte order, i.e. big endian.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ver | Type | Hop Limit | Hop Count |
| Version | Type | Hop Limit | Hop Count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Origin Address |
| |
@ -73,15 +71,15 @@ header is in network byte order, i.e. big endian.
| Target Address |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number | Metric | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Body Length |
| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
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
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
message.
Sequence number is the sequence number of either the source or target
node for this message, depending on type.
### Encryption Data
@ -203,7 +198,7 @@ A simple chat message.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |
| Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 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.

View file

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

View file

@ -13,11 +13,11 @@ import scala.collection.immutable.TreeSet
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)
@ -47,8 +47,7 @@ class MessageTest extends AndroidTestCase {
}
def testSerializeSigned(): Unit = {
val header = new MessageHeader(ConnectionInfo.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56,
new GregorianCalendar(2020, 11, 11).getTime, 0xffff, 0)
val header = new MessageHeader(ConnectionInfo.Type, 0xff, AddressTest.a4, AddressTest.a2, 0, 56)
val m = new Message(header, ConnectionInfoTest.generateCi(getContext))
val signed = Crypto.sign(m)

View file

@ -8,7 +8,10 @@ object Message {
* Orders messages by date, oldest messages first.
*/
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 = {

View file

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

View file

@ -1,6 +1,7 @@
package com.nutomic.ensichat.protocol.messages
import java.nio.ByteBuffer
import java.util.Date
import com.nutomic.ensichat.protocol.BufferUtils
@ -15,10 +16,11 @@ object Text {
*/
def read(array: Array[Byte]): Text = {
val b = ByteBuffer.wrap(array)
val time = new Date(BufferUtils.getUnsignedInt(b) * 1000)
val length = BufferUtils.getUnsignedInt(b).toInt
val bytes = new Array[Byte](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.
*/
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 write: Array[Byte] = {
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)
b.put(bytes)
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("target"))),
-1,
-1,
-1)
val body = new Text(new String(c.getString(c.getColumnIndex ("text"))),
new Date(c.getLong(c.getColumnIndex("date"))))
val body = new Text(new String(c.getString(c.getColumnIndex ("text"))))
messages += new Message(header, body)
}
c.close()
@ -71,13 +71,13 @@ class Database(context: Context) extends SQLiteOpenHelper(context, Database.Data
* Inserts the given new message into the database.
*/
def addMessage(message: Message): Unit = message.Body match {
case msg: Text =>
case text: Text =>
val cv = new ContentValues()
cv.put("origin", message.Header.Origin.toString)
cv.put("target", message.Header.Target.toString)
// toString used as workaround for compile error with Long.
cv.put("date", message.Header.Time.getTime.toString)
cv.put("text", msg.text)
cv.put("date", text.time.getTime.toString)
cv.put("text", text.text)
getWritableDatabase.insert("messages", null, cv)
case _: ConnectionInfo | _: RequestAddContact | _: ResultAddContact =>
// Never stored.