blob: ec2658ba5870d5818f819e2a492fc0fad4375cc3 [file] [log] [blame]
package coap
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"sort"
)
const (
TCP_MESSAGE_LEN13_BASE = 13
TCP_MESSAGE_LEN14_BASE = 269
TCP_MESSAGE_LEN15_BASE = 65805
TCP_MESSAGE_MAX_LEN = 0x7fff0000 // Large number that works in 32-bit builds.
)
// TcpMessage is a CoAP MessageBase that can encode itself for TCP
// transport.
type TcpMessage struct {
MessageBase
}
func NewTcpMessage(p MessageParams) *TcpMessage {
return &TcpMessage{
MessageBase{
typ: p.Type,
code: p.Code,
messageID: p.MessageID,
token: p.Token,
payload: p.Payload,
},
}
}
func (m *TcpMessage) MarshalBinary() ([]byte, error) {
/*
A CoAP TCP message looks 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 |
*/
buf := bytes.Buffer{}
sort.Stable(&m.MessageBase.opts)
writeOpts(&buf, m.MessageBase.opts)
if len(m.MessageBase.payload) > 0 {
buf.Write([]byte{0xff})
buf.Write(m.MessageBase.payload)
}
var lenNib uint8
var extLenBytes []byte
if buf.Len() < TCP_MESSAGE_LEN13_BASE {
lenNib = uint8(buf.Len())
} else if buf.Len() < TCP_MESSAGE_LEN14_BASE {
lenNib = 13
extLen := buf.Len() - TCP_MESSAGE_LEN13_BASE
extLenBytes = []byte{uint8(extLen)}
} else if buf.Len() < TCP_MESSAGE_LEN15_BASE {
lenNib = 14
extLen := buf.Len() - TCP_MESSAGE_LEN14_BASE
extLenBytes = make([]byte, 2)
binary.BigEndian.PutUint16(extLenBytes, uint16(extLen))
} else if buf.Len() < TCP_MESSAGE_MAX_LEN {
lenNib = 15
extLen := buf.Len() - TCP_MESSAGE_LEN15_BASE
extLenBytes = make([]byte, 4)
binary.BigEndian.PutUint32(extLenBytes, uint32(extLen))
}
hdr := make([]byte, 1+len(extLenBytes)+len(m.MessageBase.token)+1)
hdrOff := 0
// Length and TKL nibbles.
hdr[hdrOff] = uint8(0xf&len(m.MessageBase.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.MessageBase.code)
hdrOff++
// Token.
if len(m.MessageBase.token) > 0 {
copy(hdr[hdrOff:hdrOff+len(m.MessageBase.token)], m.MessageBase.token)
hdrOff += len(m.MessageBase.token)
}
return append(hdr, buf.Bytes()...), nil
}
// msgTcpInfo describes a single TCP CoAP message. Used during reassembly.
type msgTcpInfo struct {
typ uint8
token []byte
code uint8
hdrLen int
totLen int
}
// readTcpMsgInfo infers information about a TCP CoAP message from the first
// fragment.
func readTcpMsgInfo(r io.Reader) (msgTcpInfo, error) {
mti := msgTcpInfo{}
hdrOff := 0
var firstByte byte
if err := binary.Read(r, binary.BigEndian, &firstByte); err != nil {
return mti, err
}
hdrOff++
lenNib := (firstByte & 0xf0) >> 4
tkl := firstByte & 0x0f
var opLen int
if lenNib < TCP_MESSAGE_LEN13_BASE {
opLen = int(lenNib)
} else if lenNib == 13 {
var extLen byte
if err := binary.Read(r, binary.BigEndian, &extLen); err != nil {
return mti, err
}
hdrOff++
opLen = TCP_MESSAGE_LEN13_BASE + int(extLen)
} else if lenNib == 14 {
var extLen uint16
if err := binary.Read(r, binary.BigEndian, &extLen); err != nil {
return mti, err
}
hdrOff += 2
opLen = TCP_MESSAGE_LEN14_BASE + int(extLen)
} else if lenNib == 15 {
var extLen uint32
if err := binary.Read(r, binary.BigEndian, &extLen); err != nil {
return mti, err
}
hdrOff += 4
opLen = TCP_MESSAGE_LEN15_BASE + int(extLen)
}
mti.totLen = hdrOff + 1 + int(tkl) + opLen
if err := binary.Read(r, binary.BigEndian, &mti.code); err != nil {
return mti, err
}
hdrOff++
mti.token = make([]byte, tkl)
if _, err := io.ReadFull(r, mti.token); err != nil {
return mti, err
}
hdrOff += int(tkl)
mti.hdrLen = hdrOff
return mti, nil
}
func readTcpMsgBody(mti msgTcpInfo, r io.Reader) (options, []byte, error) {
bodyLen := mti.totLen - mti.hdrLen
b := make([]byte, bodyLen)
if _, err := io.ReadFull(r, b); err != nil {
return nil, nil, err
}
o, p, err := parseBody(b)
if err != nil {
return nil, nil, err
}
return o, p, nil
}
func (m *TcpMessage) fill(mti msgTcpInfo, o options, p []byte) {
m.MessageBase.typ = COAPType(mti.typ)
m.MessageBase.code = COAPCode(mti.code)
m.MessageBase.token = mti.token
m.MessageBase.opts = o
m.MessageBase.payload = p
}
func (m *TcpMessage) UnmarshalBinary(data []byte) error {
r := bytes.NewReader(data)
mti, err := readTcpMsgInfo(r)
if err != nil {
return fmt.Errorf("Error reading TCP CoAP header; %s", err.Error())
}
if len(data) != mti.totLen {
return fmt.Errorf("CoAP length mismatch (hdr=%d pkt=%d)",
mti.totLen, len(data))
}
o, p, err := readTcpMsgBody(mti, r)
if err != nil {
return err
}
m.fill(mti, o, p)
return nil
}
// PullTcp extracts a complete TCP CoAP message from the front of a byte queue.
//
// Return values:
// *TcpMessage: On success, points to the extracted message; nil if a complete
// message could not be extracted.
// []byte: The unread portion of of the supplied byte buffer. If a message
// was not extracted, this is the unchanged buffer that was passed in.
// error: Non-nil if the buffer contains an invalid CoAP message.
//
// Note: It is not an error if the supplied buffer does not contain a complete
// message. In such a case, nil *TclMessage and error values are returned
// along with the original buffer.
func PullTcp(data []byte) (*TcpMessage, []byte, error) {
r := bytes.NewReader(data)
m, err := Decode(r)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
// Packet is incomplete.
return nil, data, nil
} else {
// Some other error.
return nil, data, err
}
}
// Determine the number of bytes read. These bytes get trimmed from the
// front of the returned data slice.
sz, err := r.Seek(0, io.SeekCurrent)
if err != nil {
// This should never happen.
return nil, data, err
}
return m, data[sz:], nil
}
// Decode reads a single message from its input.
func Decode(r io.Reader) (*TcpMessage, error) {
mti, err := readTcpMsgInfo(r)
if err != nil {
return nil, err
}
o, p, err := readTcpMsgBody(mti, r)
if err != nil {
return nil, err
}
m := &TcpMessage{}
m.fill(mti, o, p)
return m, nil
}