mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-11 19:29:16 +08:00

* Switch CoAP lib Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Revert removed adapter code Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * WIP CoAP refactor Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add auth key Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Fix observers map Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Fix reading message body Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Fix subtopic parsing Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Fix error handling Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Fix multi-protocol communication Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Separate client from observer Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove unused config Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove TCP option Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Inline error check Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add logging client errors Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Replace RWMutex since we're not using RLock Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Inline error handling Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Inline error handling Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
345 lines
9.0 KiB
Go
345 lines
9.0 KiB
Go
package message
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
"github.com/plgd-dev/go-coap/v2/message"
|
|
"github.com/plgd-dev/go-coap/v2/message/codes"
|
|
)
|
|
|
|
const (
|
|
MESSAGE_LEN13_BASE = 13
|
|
MESSAGE_LEN14_BASE = 269
|
|
MESSAGE_LEN15_BASE = 65805
|
|
MESSAGE_MAX_LEN = 0x7fff0000 // Large number that works in 32-bit builds.
|
|
)
|
|
|
|
// Signal CSM Option IDs
|
|
/*
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
| No. | C | R | Name | Format | Length | Default |
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
| 2 | | | MaxMessageSize | uint | 0-4 | 1152 |
|
|
| 4 | | | BlockWiseTransfer | empty | 0 | (none) |
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
C=Critical, R=Repeatable
|
|
*/
|
|
|
|
const (
|
|
MaxMessageSize message.OptionID = 2
|
|
BlockWiseTransfer message.OptionID = 4
|
|
)
|
|
|
|
// Signal Ping/Pong Option IDs
|
|
/*
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
| No. | C | R | Name | Format | Length | Default |
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
| 2 | | | Custody | empty | 0 | (none) |
|
|
+-----+---+---+-------------------+--------+--------+---------+
|
|
C=Critical, R=Repeatable
|
|
*/
|
|
|
|
const (
|
|
Custody message.OptionID = 2
|
|
)
|
|
|
|
// Signal Release Option IDs
|
|
/*
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
| No. | C | R | Name | Format | Length | Default |
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
| 2 | | x | Alternative-Address | string | 1-255 | (none) |
|
|
| 4 | | | Hold-Off | uint3 | 0-3 | (none) |
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
C=Critical, R=Repeatable
|
|
*/
|
|
|
|
const (
|
|
AlternativeAddress message.OptionID = 2
|
|
HoldOff message.OptionID = 4
|
|
)
|
|
|
|
// Signal Abort Option IDs
|
|
/*
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
| No. | C | R | Name | Format | Length | Default |
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
| 2 | | | Bad-CSM-Option | uint | 0-2 | (none) |
|
|
+-----+---+---+---------------------+--------+--------+---------+
|
|
C=Critical, R=Repeatable
|
|
*/
|
|
const (
|
|
BadCSMOption message.OptionID = 2
|
|
)
|
|
|
|
var signalCSMOptionDefs = map[message.OptionID]message.OptionDef{
|
|
MaxMessageSize: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 4},
|
|
BlockWiseTransfer: {ValueFormat: message.ValueEmpty, MinLen: 0, MaxLen: 0},
|
|
}
|
|
|
|
var signalPingPongOptionDefs = map[message.OptionID]message.OptionDef{
|
|
Custody: {ValueFormat: message.ValueEmpty, MinLen: 0, MaxLen: 0},
|
|
}
|
|
|
|
var signalReleaseOptionDefs = map[message.OptionID]message.OptionDef{
|
|
AlternativeAddress: {ValueFormat: message.ValueString, MinLen: 1, MaxLen: 255},
|
|
HoldOff: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 3},
|
|
}
|
|
|
|
var signalAbortOptionDefs = map[message.OptionID]message.OptionDef{
|
|
BadCSMOption: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 2},
|
|
}
|
|
|
|
// TcpMessage is a CoAP MessageBase that can encode itself for Message
|
|
// transport.
|
|
type Message struct {
|
|
Code codes.Code
|
|
|
|
Token []byte
|
|
Payload []byte
|
|
|
|
Options message.Options //Options must be sorted by ID
|
|
}
|
|
|
|
func (m Message) Size() (int, error) {
|
|
size, err := m.MarshalTo(nil)
|
|
if err == message.ErrTooSmall {
|
|
err = nil
|
|
}
|
|
return size, err
|
|
}
|
|
|
|
func (m Message) Marshal() ([]byte, error) {
|
|
b := make([]byte, 1024)
|
|
l, err := m.MarshalTo(b)
|
|
if err == message.ErrTooSmall {
|
|
b = append(b[:0], make([]byte, l)...)
|
|
l, err = m.MarshalTo(b)
|
|
}
|
|
return b[:l], err
|
|
}
|
|
|
|
func (m Message) MarshalTo(buf []byte) (int, error) {
|
|
/*
|
|
A CoAP Message message lomessage.OKs like:
|
|
|
|
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
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Len | TKL | Extended Length ...
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Code | TKL bytes ...
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| Options (if any) ...
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|1 1 1 1 1 1 1 1| Payload (if any) ...
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
The size of the Extended Length field is inferred from the value of the
|
|
Len field as follows:
|
|
|
|
| Len value | Extended Length size | Total length |
|
|
+------------+-----------------------+---------------------------+
|
|
| 0-12 | 0 | Len |
|
|
| 13 | 1 | Extended Length + 13 |
|
|
| 14 | 2 | Extended Length + 269 |
|
|
| 15 | 4 | Extended Length + 65805 |
|
|
*/
|
|
|
|
if len(m.Token) > message.MaxTokenSize {
|
|
return -1, message.ErrInvalidTokenLen
|
|
}
|
|
|
|
payloadLen := len(m.Payload)
|
|
if payloadLen > 0 {
|
|
//for separator 0xff
|
|
payloadLen++
|
|
}
|
|
optionsLen, err := m.Options.Marshal(nil)
|
|
if err != message.ErrTooSmall {
|
|
return -1, err
|
|
}
|
|
bufLen := payloadLen + optionsLen
|
|
var lenNib uint8
|
|
var extLenBytes []byte
|
|
|
|
if bufLen < MESSAGE_LEN13_BASE {
|
|
lenNib = uint8(bufLen)
|
|
} else if bufLen < MESSAGE_LEN14_BASE {
|
|
lenNib = 13
|
|
extLen := bufLen - MESSAGE_LEN13_BASE
|
|
extLenBytes = []byte{uint8(extLen)}
|
|
} else if bufLen < MESSAGE_LEN15_BASE {
|
|
lenNib = 14
|
|
extLen := bufLen - MESSAGE_LEN14_BASE
|
|
extLenBytes = make([]byte, 2)
|
|
binary.BigEndian.PutUint16(extLenBytes, uint16(extLen))
|
|
} else if bufLen < MESSAGE_MAX_LEN {
|
|
lenNib = 15
|
|
extLen := bufLen - MESSAGE_LEN15_BASE
|
|
extLenBytes = make([]byte, 4)
|
|
binary.BigEndian.PutUint32(extLenBytes, uint32(extLen))
|
|
}
|
|
|
|
var hdr [1 + 4 + message.MaxTokenSize + 1]byte
|
|
hdrLen := 1 + len(extLenBytes) + len(m.Token) + 1
|
|
hdrOff := 0
|
|
|
|
// Length and TKL nibbles.
|
|
hdr[hdrOff] = uint8(0xf&len(m.Token)) | (lenNib << 4)
|
|
hdrOff++
|
|
|
|
// Extended length, if present.
|
|
if len(extLenBytes) > 0 {
|
|
copy(hdr[hdrOff:hdrOff+len(extLenBytes)], extLenBytes)
|
|
hdrOff += len(extLenBytes)
|
|
}
|
|
|
|
// Code.
|
|
hdr[hdrOff] = byte(m.Code)
|
|
hdrOff++
|
|
|
|
// Token.
|
|
if len(m.Token) > 0 {
|
|
copy(hdr[hdrOff:hdrOff+len(m.Token)], m.Token)
|
|
hdrOff += len(m.Token)
|
|
}
|
|
|
|
bufLen = bufLen + hdrLen
|
|
if len(buf) < bufLen {
|
|
return bufLen, message.ErrTooSmall
|
|
}
|
|
|
|
copy(buf, hdr[:hdrLen])
|
|
optionsLen, err = m.Options.Marshal(buf[hdrLen:])
|
|
switch err {
|
|
case nil:
|
|
case message.ErrTooSmall:
|
|
return bufLen, err
|
|
default:
|
|
return -1, err
|
|
}
|
|
if len(m.Payload) > 0 {
|
|
copy(buf[hdrLen+optionsLen:], []byte{0xff})
|
|
copy(buf[hdrLen+optionsLen+1:], m.Payload)
|
|
}
|
|
|
|
return bufLen, nil
|
|
}
|
|
|
|
type MessageHeader struct {
|
|
Token []byte
|
|
Code codes.Code
|
|
HeaderLen int
|
|
TotalLen int
|
|
}
|
|
|
|
// Unmarshal infers information about a Message CoAP message from the first
|
|
// fragment.
|
|
func (i *MessageHeader) Unmarshal(data []byte) error {
|
|
hdrOff := 0
|
|
if len(data) == 0 {
|
|
return message.ErrShortRead
|
|
}
|
|
|
|
firstByte := data[0]
|
|
data = data[1:]
|
|
hdrOff++
|
|
|
|
lenNib := (firstByte & 0xf0) >> 4
|
|
tkl := firstByte & 0x0f
|
|
|
|
var opLen int
|
|
switch {
|
|
case lenNib < MESSAGE_LEN13_BASE:
|
|
opLen = int(lenNib)
|
|
case lenNib == 13:
|
|
if len(data) < 1 {
|
|
return message.ErrShortRead
|
|
}
|
|
extLen := data[0]
|
|
data = data[1:]
|
|
hdrOff++
|
|
opLen = MESSAGE_LEN13_BASE + int(extLen)
|
|
case lenNib == 14:
|
|
if len(data) < 2 {
|
|
return message.ErrShortRead
|
|
}
|
|
extLen := binary.BigEndian.Uint16(data)
|
|
data = data[2:]
|
|
hdrOff += 2
|
|
opLen = MESSAGE_LEN14_BASE + int(extLen)
|
|
case lenNib == 15:
|
|
if len(data) < 4 {
|
|
return message.ErrShortRead
|
|
}
|
|
extLen := binary.BigEndian.Uint32(data)
|
|
data = data[4:]
|
|
hdrOff += 4
|
|
opLen = MESSAGE_LEN15_BASE + int(extLen)
|
|
}
|
|
|
|
i.TotalLen = hdrOff + 1 + int(tkl) + opLen
|
|
if len(data) < 1 {
|
|
return message.ErrShortRead
|
|
}
|
|
i.Code = codes.Code(data[0])
|
|
data = data[1:]
|
|
hdrOff++
|
|
if len(data) < int(tkl) {
|
|
return message.ErrShortRead
|
|
}
|
|
if tkl > 0 {
|
|
i.Token = data[:tkl]
|
|
}
|
|
hdrOff += int(tkl)
|
|
|
|
i.HeaderLen = hdrOff
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Message) UnmarshalWithHeader(header MessageHeader, data []byte) (int, error) {
|
|
optionDefs := message.CoapOptionDefs
|
|
processed := header.HeaderLen
|
|
switch codes.Code(header.Code) {
|
|
case codes.CSM:
|
|
optionDefs = signalCSMOptionDefs
|
|
case codes.Ping, codes.Pong:
|
|
optionDefs = signalPingPongOptionDefs
|
|
case codes.Release:
|
|
optionDefs = signalReleaseOptionDefs
|
|
case codes.Abort:
|
|
optionDefs = signalAbortOptionDefs
|
|
}
|
|
|
|
proc, err := m.Options.Unmarshal(data, optionDefs)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
data = data[proc:]
|
|
processed += proc
|
|
|
|
if len(data) > 0 {
|
|
m.Payload = data
|
|
}
|
|
processed = processed + len(data)
|
|
m.Code = header.Code
|
|
m.Token = header.Token
|
|
|
|
return processed, nil
|
|
}
|
|
|
|
func (m *Message) Unmarshal(data []byte) (int, error) {
|
|
header := MessageHeader{Token: m.Token}
|
|
err := header.Unmarshal(data)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
if len(data) < header.TotalLen {
|
|
return -1, message.ErrShortRead
|
|
}
|
|
return m.UnmarshalWithHeader(header, data[header.HeaderLen:])
|
|
}
|