More protocols
This commit is contained in:
302
protocol/meshcore/packet.go
Normal file
302
protocol/meshcore/packet.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package meshcore
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
minPacketSize = 2
|
||||
maxPathSize = 64
|
||||
maxPayloadSize = 184
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
// SNR is the signal-to-noise ratio.
|
||||
SNR float64 `json:"snr"`
|
||||
|
||||
// RSSI is the received signal strength indicator (in dBm).
|
||||
RSSI int8 `json:"rssi"`
|
||||
|
||||
// Raw bytes (optional).
|
||||
Raw []byte `json:"raw,omitempty"`
|
||||
|
||||
// RouteType is the type of route for this packet.
|
||||
RouteType RouteType `json:"route_type"`
|
||||
|
||||
// PayloadType is the type of payload for this packet.
|
||||
PayloadType PayloadType `json:"payload_type"`
|
||||
|
||||
// TransportCodes are set by transport route types.
|
||||
TransportCodes []uint16 `json:"transport_codes,omitempty"`
|
||||
|
||||
// Path are repeater hashes.
|
||||
Path []byte `json:"path"`
|
||||
|
||||
// Payload is the raw (encoded) payload.
|
||||
Payload []byte `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
func Decode(data []byte) (Payload, error) {
|
||||
packet := new(Packet)
|
||||
if err := packet.UnmarshalBytes(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return packet.Decode()
|
||||
}
|
||||
|
||||
func (packet *Packet) Decode() (Payload, error) {
|
||||
var payload Payload
|
||||
switch packet.PayloadType {
|
||||
case TypeRequest:
|
||||
payload = &Request{Raw: packet}
|
||||
case TypeResponse:
|
||||
payload = &Response{Raw: packet}
|
||||
case TypeText:
|
||||
payload = &Text{Raw: packet}
|
||||
case TypeAck:
|
||||
payload = &Acknowledgement{Raw: packet}
|
||||
case TypeAdvert:
|
||||
payload = &Advert{Raw: packet}
|
||||
case TypeGroupText:
|
||||
payload = &GroupText{Raw: packet}
|
||||
case TypeGroupData:
|
||||
payload = &GroupData{Raw: packet}
|
||||
case TypeAnonRequest:
|
||||
payload = &AnonymousRequest{Raw: packet}
|
||||
case TypePath:
|
||||
payload = &Path{Raw: packet}
|
||||
case TypeTrace:
|
||||
payload = &Trace{Raw: packet}
|
||||
case TypeMultipart:
|
||||
payload = &Multipart{Raw: packet}
|
||||
case TypeControl:
|
||||
payload = &Control{Raw: packet}
|
||||
case TypeRawCustom:
|
||||
payload = &RawCustom{Raw: packet}
|
||||
default:
|
||||
return nil, fmt.Errorf("meshcore: invalid payload type %#02x", packet.PayloadType)
|
||||
}
|
||||
|
||||
if err := payload.Unmarshal(packet.Payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (packet *Packet) String() string {
|
||||
s := []string{
|
||||
packet.RouteType.String(),
|
||||
packet.PayloadType.String(),
|
||||
}
|
||||
if len(packet.TransportCodes) == 2 {
|
||||
s = append(s, fmt.Sprintf("%02X%02X", packet.TransportCodes[0], packet.TransportCodes[1]))
|
||||
}
|
||||
if len(packet.Path) > 0 {
|
||||
s = append(s, formatPath(packet.Path))
|
||||
} else {
|
||||
s = append(s, "direct")
|
||||
}
|
||||
return strings.Join(s, " ")
|
||||
}
|
||||
|
||||
func (packet *Packet) MarshalBytes() []byte {
|
||||
var (
|
||||
data [1 + 4 + 1 + maxPathSize + maxPayloadSize]byte
|
||||
offset int
|
||||
)
|
||||
data[offset] = byte(packet.RouteType&0x03) | byte((packet.PayloadType&0x0f)<<2)
|
||||
offset += 1
|
||||
|
||||
if packet.RouteType.HasTransportCodes() {
|
||||
binary.LittleEndian.PutUint16(data[offset:], packet.TransportCodes[0])
|
||||
offset += 2
|
||||
binary.LittleEndian.PutUint16(data[offset:], packet.TransportCodes[1])
|
||||
offset += 2
|
||||
}
|
||||
|
||||
data[offset] = byte(len(packet.Path))
|
||||
offset += 1
|
||||
offset += copy(data[offset:], packet.Path)
|
||||
offset += copy(data[offset:], packet.Payload)
|
||||
return data[:offset]
|
||||
}
|
||||
|
||||
func (packet *Packet) UnmarshalBytes(data []byte) error {
|
||||
if len(data) < minPacketSize {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
packet.Raw = make([]byte, len(data))
|
||||
copy(packet.Raw, data)
|
||||
|
||||
packet.RouteType = RouteType(data[0] & 0x03)
|
||||
packet.PayloadType = PayloadType((data[0] >> 2) & 0x0f)
|
||||
offset := 1
|
||||
|
||||
if packet.RouteType.HasTransportCodes() {
|
||||
if len(data) < minPacketSize+4 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
packet.TransportCodes = []uint16{
|
||||
binary.LittleEndian.Uint16(data[offset+0:]),
|
||||
binary.LittleEndian.Uint16(data[offset+2:]),
|
||||
}
|
||||
offset += 4
|
||||
} else {
|
||||
packet.TransportCodes = nil
|
||||
}
|
||||
|
||||
pathLength := int(data[offset])
|
||||
offset += 1
|
||||
if pathLength > maxPathSize {
|
||||
return fmt.Errorf("meshcore: path length %d exceeds maximum of %d", pathLength, maxPathSize)
|
||||
} else if pathLength > len(data[offset:]) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
packet.Path = make([]byte, pathLength)
|
||||
offset += copy(packet.Path, data[offset:])
|
||||
|
||||
payloadLength := len(data[offset:])
|
||||
if payloadLength > maxPayloadSize {
|
||||
return fmt.Errorf("meshcore: payload length %d exceeds maximum of %d", payloadLength, maxPayloadSize)
|
||||
}
|
||||
packet.Payload = make([]byte, payloadLength)
|
||||
copy(packet.Payload, data[offset:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (packet *Packet) Hash() []byte {
|
||||
h := sha256.New()
|
||||
h.Write([]byte{byte(packet.PayloadType)})
|
||||
if packet.PayloadType == TypeTrace {
|
||||
h.Write(packet.Path)
|
||||
}
|
||||
h.Write(packet.Payload)
|
||||
return h.Sum(nil)[:8]
|
||||
}
|
||||
|
||||
func (packet *Packet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
SNR float64 `json:"snr"`
|
||||
RSSI int8 `json:"rssi"`
|
||||
RouteType RouteType `json:"route_type"`
|
||||
PayloadType PayloadType `json:"payload_type"`
|
||||
TransportCodes []uint16 `json:"transport_codes,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Payload string `json:"payload,omitempty"`
|
||||
Hash string `json:"hash"`
|
||||
}{
|
||||
packet.SNR,
|
||||
packet.RSSI,
|
||||
packet.RouteType,
|
||||
packet.PayloadType,
|
||||
packet.TransportCodes,
|
||||
hex.EncodeToString(packet.Path),
|
||||
hex.EncodeToString(packet.Payload),
|
||||
hex.EncodeToString(packet.Hash()),
|
||||
})
|
||||
}
|
||||
|
||||
type RouteType byte
|
||||
|
||||
// Route types.
|
||||
const (
|
||||
TransportFlood RouteType = iota
|
||||
Flood
|
||||
Direct
|
||||
TransportDirect
|
||||
)
|
||||
|
||||
// HasTransportCodes indicates if this route type has transport codes in the packet.
|
||||
func (rt RouteType) HasTransportCodes() bool {
|
||||
return rt == TransportFlood || rt == TransportDirect
|
||||
}
|
||||
|
||||
// IsDirect is a direct routing type.
|
||||
func (rt RouteType) IsDirect() bool {
|
||||
return rt == Direct || rt == TransportDirect
|
||||
}
|
||||
|
||||
// IsFlood is a flood routing type.
|
||||
func (rt RouteType) IsFlood() bool {
|
||||
return rt == Flood || rt == TransportFlood
|
||||
}
|
||||
|
||||
func (rt RouteType) String() string {
|
||||
switch rt {
|
||||
case TransportFlood:
|
||||
return "F[T]"
|
||||
case Flood:
|
||||
return "F"
|
||||
case Direct:
|
||||
return "D"
|
||||
case TransportDirect:
|
||||
return "D[T]"
|
||||
default:
|
||||
return "?"
|
||||
}
|
||||
}
|
||||
|
||||
type PayloadType byte
|
||||
|
||||
// Payload types.
|
||||
const (
|
||||
TypeRequest PayloadType = iota
|
||||
TypeResponse
|
||||
TypeText
|
||||
TypeAck
|
||||
TypeAdvert
|
||||
TypeGroupText
|
||||
TypeGroupData
|
||||
TypeAnonRequest
|
||||
TypePath
|
||||
TypeTrace
|
||||
TypeMultipart
|
||||
TypeControl
|
||||
_ // reserved
|
||||
_ // reserved
|
||||
_ // reserved
|
||||
TypeRawCustom
|
||||
)
|
||||
|
||||
func (pt PayloadType) String() string {
|
||||
switch pt {
|
||||
case TypeRequest:
|
||||
return "request"
|
||||
case TypeResponse:
|
||||
return "response"
|
||||
case TypeText:
|
||||
return "text"
|
||||
case TypeAck:
|
||||
return "ack"
|
||||
case TypeAdvert:
|
||||
return "advert"
|
||||
case TypeGroupText:
|
||||
return "group text"
|
||||
case TypeGroupData:
|
||||
return "group data"
|
||||
case TypeAnonRequest:
|
||||
return "anon request"
|
||||
case TypePath:
|
||||
return "path"
|
||||
case TypeTrace:
|
||||
return "trace"
|
||||
case TypeMultipart:
|
||||
return "multipart"
|
||||
case TypeControl:
|
||||
return "control"
|
||||
case TypeRawCustom:
|
||||
return "raw custom"
|
||||
default:
|
||||
return fmt.Sprintf("invalid %02x", byte(pt))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user