Files
agent-call/services/asr-web/volc_frame.go
T
2026-09-11 17:47:03 +08:00

61 lines
1.5 KiB
Go

package main
import (
"bytes"
"compress/gzip"
"encoding/binary"
"errors"
"io"
)
// Reject truncated, oversized or compressed-bomb frames before slicing payloads.
func decodeVolc(data []byte) (typ, flags byte, code uint32, payload []byte, err error) {
invalid := errors.New("invalid ASR frame")
if len(data) < 8 || data[0]>>4 != 1 {
return 0, 0, 0, nil, invalid
}
typ, flags = data[1]>>4, data[1]&15
off := int(data[0]&15) * 4
if off < 4 || off > len(data) || (typ != msgFullServer && typ != msgError) {
return 0, 0, 0, nil, invalid
}
if flags&1 != 0 {
off += 4
} // Bit 1 means last packet; it does not imply a sequence field.
if off > len(data) {
return 0, 0, 0, nil, invalid
}
if typ == msgError {
if off+4 > len(data) {
return 0, 0, 0, nil, invalid
}
code = binary.BigEndian.Uint32(data[off : off+4])
off += 4
}
if off+4 > len(data) {
return 0, 0, 0, nil, invalid
}
size := uint64(binary.BigEndian.Uint32(data[off : off+4]))
off += 4
if size > 1<<20 || size != uint64(len(data)-off) {
return 0, 0, 0, nil, invalid
}
payload = data[off:]
switch data[2] & 15 {
case compNone:
case compGzip:
reader, e := gzip.NewReader(bytes.NewReader(payload))
if e != nil {
return 0, 0, 0, nil, invalid
}
payload, e = io.ReadAll(io.LimitReader(reader, (1<<20)+1))
closeErr := reader.Close()
if e != nil || closeErr != nil || len(payload) > 1<<20 {
return 0, 0, 0, nil, invalid
}
default:
return 0, 0, 0, nil, invalid
}
return typ, flags, code, payload, nil
}