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 }