144 lines
3.5 KiB
Go
144 lines
3.5 KiB
Go
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package proto
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// ChannelData represents The ChannelData Message.
|
|
//
|
|
// See RFC 5766 Section 11.4
|
|
type ChannelData struct {
|
|
Data []byte // Can be sub slice of Raw
|
|
Length int // Ignored while encoding, len(Data) is used
|
|
Number ChannelNumber
|
|
Raw []byte
|
|
}
|
|
|
|
// Equal returns true if b == c.
|
|
func (c *ChannelData) Equal(b *ChannelData) bool {
|
|
if c == nil && b == nil {
|
|
return true
|
|
}
|
|
if c == nil || b == nil {
|
|
return false
|
|
}
|
|
if c.Number != b.Number {
|
|
return false
|
|
}
|
|
if len(c.Data) != len(b.Data) {
|
|
return false
|
|
}
|
|
return bytes.Equal(c.Data, b.Data)
|
|
}
|
|
|
|
// Grow ensures that internal buffer will fit v more bytes and
|
|
// increases it capacity if necessary.
|
|
//
|
|
// Similar to stun.Message.grow method.
|
|
func (c *ChannelData) grow(v int) {
|
|
n := len(c.Raw) + v
|
|
for cap(c.Raw) < n {
|
|
c.Raw = append(c.Raw, 0)
|
|
}
|
|
c.Raw = c.Raw[:n]
|
|
}
|
|
|
|
// Reset resets Length, Data and Raw length.
|
|
func (c *ChannelData) Reset() {
|
|
c.Raw = c.Raw[:0]
|
|
c.Length = 0
|
|
c.Data = c.Data[:0]
|
|
}
|
|
|
|
// Encode encodes ChannelData Message to Raw.
|
|
func (c *ChannelData) Encode() {
|
|
c.Raw = c.Raw[:0]
|
|
c.WriteHeader()
|
|
c.Raw = append(c.Raw, c.Data...)
|
|
padded := nearestPaddedValueLength(len(c.Raw))
|
|
if bytesToAdd := padded - len(c.Raw); bytesToAdd > 0 {
|
|
for i := 0; i < bytesToAdd; i++ {
|
|
c.Raw = append(c.Raw, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
const padding = 4
|
|
|
|
func nearestPaddedValueLength(l int) int {
|
|
n := padding * (l / padding)
|
|
if n < l {
|
|
n += padding
|
|
}
|
|
return n
|
|
}
|
|
|
|
// WriteHeader writes channel number and length.
|
|
func (c *ChannelData) WriteHeader() {
|
|
if len(c.Raw) < channelDataHeaderSize {
|
|
// Making WriteHeader call valid even when c.Raw
|
|
// is nil or len(c.Raw) is less than needed for header.
|
|
c.grow(channelDataHeaderSize)
|
|
}
|
|
// Early bounds check to guarantee safety of writes below.
|
|
_ = c.Raw[:channelDataHeaderSize]
|
|
binary.BigEndian.PutUint16(c.Raw[:channelDataNumberSize], uint16(c.Number))
|
|
binary.BigEndian.PutUint16(c.Raw[channelDataNumberSize:channelDataHeaderSize],
|
|
uint16(len(c.Data)),
|
|
)
|
|
}
|
|
|
|
// ErrBadChannelDataLength means that channel data length is not equal
|
|
// to actual data length.
|
|
var ErrBadChannelDataLength = errors.New("channelData length != len(Data)")
|
|
|
|
// Decode decodes The ChannelData Message from Raw.
|
|
func (c *ChannelData) Decode() error {
|
|
buf := c.Raw
|
|
if len(buf) < channelDataHeaderSize {
|
|
return io.ErrUnexpectedEOF
|
|
}
|
|
num := binary.BigEndian.Uint16(buf[:channelDataNumberSize])
|
|
c.Number = ChannelNumber(num)
|
|
l := binary.BigEndian.Uint16(buf[channelDataNumberSize:channelDataHeaderSize])
|
|
c.Data = buf[channelDataHeaderSize:]
|
|
c.Length = int(l)
|
|
if !c.Number.Valid() {
|
|
return ErrInvalidChannelNumber
|
|
}
|
|
if int(l) < len(c.Data) {
|
|
c.Data = c.Data[:int(l)]
|
|
}
|
|
if int(l) > len(buf[channelDataHeaderSize:]) {
|
|
return ErrBadChannelDataLength
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
channelDataLengthSize = 2
|
|
channelDataNumberSize = channelDataLengthSize
|
|
channelDataHeaderSize = channelDataLengthSize + channelDataNumberSize
|
|
)
|
|
|
|
// IsChannelData returns true if buf looks like the ChannelData Message.
|
|
func IsChannelData(buf []byte) bool {
|
|
if len(buf) < channelDataHeaderSize {
|
|
return false
|
|
}
|
|
|
|
if int(binary.BigEndian.Uint16(buf[channelDataNumberSize:channelDataHeaderSize])) > len(buf[channelDataHeaderSize:]) {
|
|
return false
|
|
}
|
|
|
|
// Quick check for channel number.
|
|
num := binary.BigEndian.Uint16(buf[0:channelNumberSize])
|
|
return isChannelNumberValid(num)
|
|
}
|