diff --git a/header_extension.go b/header_extension.go index 174bd4e..5ed654b 100644 --- a/header_extension.go +++ b/header_extension.go @@ -10,6 +10,18 @@ import ( "io" ) +/* + RFC 3550, RTP Header Extension: + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | defined by profile | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | header extension | + | .... | +*/ + const ( headerExtensionIDReserved = 0xF ) @@ -49,6 +61,10 @@ func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error { payloadLen := int(e.payload[n]&^0xF0 + 1) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], append(buf, e.payload[n+payloadLen:]...)...) @@ -70,6 +86,9 @@ func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error { // GetIDs returns the available IDs. func (e *OneByteHeaderExtension) GetIDs() []uint8 { + if len(e.payload) < 4 { + return nil + } ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) for n := 4; n < len(e.payload); { if e.payload[n] == 0x00 { // padding @@ -86,6 +105,10 @@ func (e *OneByteHeaderExtension) GetIDs() []uint8 { break } + if n+payloadLen > len(e.payload) { + break + } + ids = append(ids, extid) n += payloadLen } @@ -106,6 +129,10 @@ func (e *OneByteHeaderExtension) Get(id uint8) []byte { payloadLen := int(e.payload[n]&^0xF0 + 1) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { return e.payload[n : n+payloadLen] } @@ -127,6 +154,10 @@ func (e *OneByteHeaderExtension) Del(id uint8) error { extid := e.payload[n] >> 4 payloadLen := int(e.payload[n]&^0xF0 + 1) + if n+1+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], e.payload[n+1+payloadLen:]...) @@ -140,10 +171,17 @@ func (e *OneByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile != ExtensionProfileOneByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil @@ -190,9 +228,17 @@ func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], append(buf, e.payload[n+payloadLen:]...)...) @@ -214,6 +260,9 @@ func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error { // GetIDs returns the available IDs. func (e *TwoByteHeaderExtension) GetIDs() []uint8 { + if len(e.payload) < 4 { + return nil + } ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) for n := 4; n < len(e.payload); { if e.payload[n] == 0x00 { // padding @@ -225,9 +274,17 @@ func (e *TwoByteHeaderExtension) GetIDs() []uint8 { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + ids = append(ids, extid) n += payloadLen } @@ -247,9 +304,17 @@ func (e *TwoByteHeaderExtension) Get(id uint8) []byte { extid := e.payload[n] n++ + if n >= len(e.payload) { + break + } + payloadLen := int(e.payload[n]) n++ + if n+payloadLen > len(e.payload) { + break + } + if extid == id { return e.payload[n : n+payloadLen] } @@ -270,8 +335,16 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error { extid := e.payload[n] + if n+1 >= len(e.payload) { + break + } + payloadLen := int(e.payload[n+1]) + if n+2+payloadLen > len(e.payload) { + break + } + if extid == id { e.payload = append(e.payload[:n], e.payload[n+2+payloadLen:]...) @@ -285,10 +358,17 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile != ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil @@ -357,10 +437,17 @@ func (e *RawExtension) Del(id uint8) error { // Unmarshal parses the extension from the given buffer. func (e *RawExtension) Unmarshal(buf []byte) (int, error) { + if len(buf) < 4 { + return 0, errTooSmall + } profile := binary.BigEndian.Uint16(buf[0:2]) if profile == ExtensionProfileOneByte || profile == ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } + extLen := binary.BigEndian.Uint16(buf[2:4]) + if len(buf) < 4+int(extLen)*4 { + return 0, errTooSmall + } e.payload = buf return len(buf), nil diff --git a/header_extension_test.go b/header_extension_test.go index d7a228b..575862d 100644 --- a/header_extension_test.go +++ b/header_extension_test.go @@ -303,7 +303,8 @@ func TestHeaderExtension_RFC8285TwoByteExtensionRewrite(t *testing.T) { func TestHeaderExtension_Raw(t *testing.T) { ext := &RawExtension{} - expectedPayload := []byte{0xBE, 0xEF} + // 4-byte raw extension: profile=0xBEEF (not one-byte/two-byte), length=0 words + expectedPayload := []byte{0xBE, 0xEF, 0x00, 0x00} assert.Error(t, ext.Set(5, expectedPayload)) assert.NoError(t, ext.Set(0, expectedPayload))