diff --git a/rtmp/amf/amf.go b/rtmp/amf/amf.go index 745c39af..0564b04c 100644 --- a/rtmp/amf/amf.go +++ b/rtmp/amf/amf.go @@ -90,6 +90,7 @@ var ( ErrUnimplemented = errors.New("amf: unimplemented feature") ErrDecodingName = errors.New("amf: name decoding error") ErrDecodingString = errors.New("amf: string decoding error") + ErrUnexpectedEnd = errors.New("amf: unexpected end") ) // Basic decoding funcions. @@ -271,7 +272,6 @@ func PropGetObject(prop *Property, obj *Object) { } // PropEncode encodes a property. - func PropEncode(p *Property, dst []byte) ([]byte, error) { if p.dtype == typeInvalid { return nil, ErrShortBuffer @@ -314,128 +314,81 @@ func PropEncode(p *Property, dst []byte) ([]byte, error) { return dst, err } -// PropDecode decodes a property, returning the number of bytes consumed from the data buffer. -func PropDecode(prop *Property, data []byte, decodeName int32) int32 { +// PropDecode decodes a property, returning the number of bytes consumed from the supplied buffer. +func PropDecode(prop *Property, data []byte, decodeName bool) (int, error) { prop.name = "" - nOriginalSize := len(data) + sz := len(data) if len(data) == 0 { - // TODO use new logger here - // RTMLog(RTMLOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); - return -1 + return 0, ErrEndOfBuffer } - if decodeName != 0 && len(data) < 4 { - // at least name (length + at least 1 byte) and 1 byte of data - // TODO use new logger here - // RTMLog(RTMLOGDEBUG, "%s: Not enough data for decoding with name, less than 4 bytes!",__FUNCTION__); - return -1 - } - - if decodeName != 0 { - nNameSize := DecodeInt16(data[:2]) - if int(nNameSize) > len(data)-2 { - // TODO use new logger here - //RTMLog(RTMLOGDEBUG, "%s: Name size out of range: namesize (%d) > len (%d) - 2",__FUNCTION__, nNameSize, nSize); - return -1 + if decodeName { + if len(data) < 4 { + return 0, ErrShortBuffer + } + n := DecodeInt16(data[:2]) + if int(n) > len(data)-2 { + return 0, ErrDecodingName } prop.name = DecodeString(data) - data = data[2+nNameSize:] + data = data[2+n:] } if len(data) == 0 { - return -1 + return 0, ErrEndOfBuffer } prop.dtype = uint8(data[0]) data = data[1:] - var nRes int32 switch prop.dtype { case typeNumber: if len(data) < 8 { - return -1 + return 0, ErrShortBuffer } prop.number = DecodeNumber(data[:8]) data = data[8:] case typeBoolean: - panic("typeBoolean not supported") + return 0, ErrUnimplemented case typeString: - nStringSize := DecodeInt16(data[:2]) - if len(data) < int(nStringSize+2) { - return -1 + n := DecodeInt16(data[:2]) + if len(data) < int(n+2) { + return 0, ErrShortBuffer } prop.str = DecodeString(data) - data = data[2+nStringSize:] + data = data[2+n:] case TypeObject: - nRes := Decode(&prop.obj, data, 1) - if nRes == -1 { - return -1 + n, err := Decode(&prop.obj, data, true) + if err != nil { + return 0, err } - data = data[nRes:] + data = data[n:] - case typeMovieClip: - // TODO use new logger here - // ??? log.Println("PropDecode: MAF_MOVIECLIP reserved!") - //RTMLog(RTMLOGERROR, "MovieClip reserved!"); - return -1 case TypeNull, typeUndefined, typeUnsupported: prop.dtype = TypeNull - case typeReference: - // TODO use new logger here - // ??? log.Println("PropDecode: Reference not supported!") - //RTMLog(RTMLOGERROR, "Reference not supported!"); - return -1 - case typeEcmaArray: - // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object data = data[4:] - nRes = Decode(&prop.obj, data, 1) - if nRes == -1 { - return -1 + n, err := Decode(&prop.obj, data, true) + if err != nil { + return 0, err } - data = data[nRes:] + data = data[n:] case TypeObjectEnd: - return -1 - - case typeStrictArray: - panic("StrictArray not supported") - - case typeDate: - panic("Date not supported") - - case typeLongString, typeXmlDoc: - panic("typeLongString, XmlDoc not supported") - - case typeRecordset: - // TODO use new logger here - // ??? log.Println("PropDecode: Recordset reserved!") - //RTMLog(RTMLOGERROR, "Recordset reserved!"); - return -1 - - case typeTypedObject: - // TODO use new logger here - // RTMLog(RTMLOGERROR, "Typed_object not supported!") - return -1 - - case typeAvmplus: - panic("Avmplus not supported") + return 0, ErrUnexpectedEnd default: - // TODO use new logger here - //RTMLog(RTMLOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, - //prop.dtype, pBuffer - 1); - return -1 + return 0, ErrUnimplemented } - return int32(nOriginalSize - len(data)) + return sz - len(data), nil } func PropReset(prop *Property) { @@ -516,8 +469,8 @@ func EncodeArray(obj *Object, dst []byte) ([]byte, error) { return dst, nil } -func Decode(obj *Object, data []byte, decodeName int32) int32 { - nOriginalSize := len(data) +func Decode(obj *Object, data []byte, decodeName bool) (int, error) { + sz := len(data) obj.Props = obj.Props[:0] for len(data) != 0 { @@ -527,17 +480,15 @@ func Decode(obj *Object, data []byte, decodeName int32) int32 { } var prop Property - nRes := PropDecode(&prop, data, decodeName) - // nRes = int32(C.PropDecode(&prop, (*byte)(unsafe.Pointer(pBuffer)), - // int32(nSize), int32(decodeName))) - if nRes == -1 { - return -1 + n, err := PropDecode(&prop, data, decodeName) + if err != nil { + return 0, err } - data = data[nRes:] + data = data[n:] obj.Props = append(obj.Props, prop) } - return int32(nOriginalSize - len(data)) + return sz - len(data), nil } func GetProp(obj *Object, name string, idx int32) *Property { diff --git a/rtmp/amf/amf_test.go b/rtmp/amf/amf_test.go index 2d3d306c..66972129 100644 --- a/rtmp/amf/amf_test.go +++ b/rtmp/amf/amf_test.go @@ -107,12 +107,12 @@ func TestProperties(t *testing.T) { } } - var n int32 + prop := Property{} dec := buf[:] for i, _ := range testNumbers { - n = PropDecode(&prop, dec, 0) - if n < 0 { + n, err := PropDecode(&prop, dec, false) + if err != nil { t.Errorf("PropDecode of number failed") } if int32(prop.number) != testNumbers[i] { @@ -133,8 +133,8 @@ func TestProperties(t *testing.T) { prop = Property{} dec = buf[:] for i, _ := range testStrings { - n = PropDecode(&prop, dec, 0) - if n < 0 { + n, err := PropDecode(&prop, dec, false) + if err != nil { t.Errorf("PropDecode of string failed") } if prop.str != testStrings[i] { @@ -181,8 +181,8 @@ func TestObject(t *testing.T) { // Decode it dec := buf[1:] var dobj1 Object - n := Decode(&dobj1, dec, 0) - if n < 0 { + _, err = Decode(&dobj1, dec, false) + if err != nil { t.Errorf("Decode of object failed") } @@ -203,8 +203,8 @@ func TestObject(t *testing.T) { // Decode it. dec = buf[1:] var dobj2 Object - n = Decode(&dobj2, dec, 0) - if n < 0 { + _, err = Decode(&dobj2, dec, false) + if err != nil { t.Errorf("Decode of object failed") } } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index c433b63a..e78a89f0 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -161,7 +161,6 @@ var ( errInvalidHeader = errors.New("rtmp: invalid header") errInvalidBody = errors.New("rtmp: invalid body") errInvalidFlvTag = errors.New("rtmp: invalid FLV tag") - errDecoding = errors.New("rtmp: decoding error") errUnimplemented = errors.New("rtmp: unimplemented feature") ) @@ -682,9 +681,9 @@ func handleInvoke(s *Session, body []byte) error { return errInvalidBody } var obj amf.Object - nRes := amf.Decode(&obj, body, 0) - if nRes < 0 { - return errDecoding + _, err := amf.Decode(&obj, body, false) + if err != nil { + return err } meth := amf.PropGetString(amf.GetProp(&obj, "", 0))