1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-11 19:29:16 +08:00
Dušan Borovčanin f10e49e6b5
MF-928 - Change CoAP lib (#1233)
* 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>
2020-09-22 11:59:10 +02:00

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:])
}