diff --git a/codec_test.go b/codec_test.go index fe02d1f..2757161 100644 --- a/codec_test.go +++ b/codec_test.go @@ -288,6 +288,44 @@ var testcases = []struct { return v, nil }, }, { + description: "Dialogue/AARQ/UserInformation", + structured: tcap.NewDialogue( + 1, 1, // OID, Version + tcap.NewAARQ( + // Version, Context, ContextVersion + 1, tcap.AnyTimeInfoEnquiryContext, 3, + tcap.NewIE(0xBE, []byte{0xde, 0xad, 0xbe, 0xef}), + ), + []byte{0xde, 0xad, 0xbe, 0xef}, + ), + serialized: []byte{ + 0x6b, 0x28, 0x28, 0x26, 0x06, 0x07, 0x00, 0x11, 0x86, 0x05, 0x01, 0x01, 0x01, 0xa0, 0x17, 0x60, + 0x15, 0x80, 0x02, 0x07, 0x80, 0xa1, 0x09, 0x06, 0x07, 0x04, 0x00, 0x00, 0x01, 0x00, 0x1d, 0x03, + 0xbe, 0x04, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + }, + parseFunc: func(b []byte) (serializable, error) { + v, err := tcap.ParseDialogue(b) + if err != nil { + return nil, err + } + + b, err = v.MarshalBinary() + if err != nil { + return nil, err + } + + // Purposely do not clean v.SingleAsn1Type.Value the first time + + v, err = tcap.ParseDialogue(b) + if err != nil { + return nil, err + } + + v.SingleAsn1Type.Value = nil + + return v, nil + }, + }, { description: "Dialogue/AARE", structured: tcap.NewDialogue( 1, 1, // OID, Version @@ -374,6 +412,33 @@ var testcases = []struct { serialized: []byte{0x48, 0x04, 0xde, 0xad, 0xbe, 0xef}, parseFunc: func(b []byte) (serializable, error) { return tcap.ParseIE(b) }, }, + { + description: "IE/UserInformation", + structured: func() *tcap.IE { + dialoguePDU := tcap.NewIE(0xa0, []byte{0xde, 0xad, 0xbe, 0xef}) + + singleAsn1Type := tcap.NewIE(0xa0, []byte{}) + singleAsn1Type.IE = []*tcap.IE{dialoguePDU} + + directReference := tcap.NewIE(0x06, []byte{04, 00, 00, 01, 01, 01, 01}) + + userInfoItem := tcap.NewIE(0x28, []byte{}) + userInfoItem.IE = []*tcap.IE{directReference, singleAsn1Type} + + userInfo := tcap.NewIE(0xBE, []byte{}) + userInfo.IE = []*tcap.IE{userInfoItem} + + userInfo.SetLength() + + return userInfo + }(), + serialized: []byte{ + 0xbe, 0x13, 0x28, 0x11, 0x06, 0x07, 0x04, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0xa0, + 0x06, 0xa0, 0x04, 0xde, 0xad, 0xbe, 0xef, + }, + parseFunc: func(b []byte) (serializable, error) { return tcap.ParseIERecursive(b) }, + }, } func TestCodec(t *testing.T) { diff --git a/component.go b/component.go index 49f2bcf..3a03ba6 100644 --- a/component.go +++ b/component.go @@ -369,6 +369,12 @@ func (c *Components) UnmarshalBinary(b []byte) error { } b = b[offset+comp.MarshalLen()-2:] } + + c.SetLength() + if b[1] != c.Length { + return fmt.Errorf("decoded Length is not equal to Components Length, got %d, expected %d", c.Length, b[1]) + } + return nil } @@ -388,6 +394,7 @@ func (c *Component) UnmarshalBinary(b []byte) error { } c.Type = Tag(b[0]) c.Length = b[1] + expectedLength := b[1] var err error var offset = 2 @@ -434,12 +441,16 @@ func (c *Component) UnmarshalBinary(b []byte) error { offset += c.OperationCode.MarshalLen() if offset >= len(b) { + c.ResultRetres.Value = []byte{} return nil } c.Parameter, err = ParseIERecursive(b[offset:]) if err != nil { return err } + offset += c.Parameter.MarshalLen() + c.ResultRetres.Value = c.ResultRetres.Value[offset:] + case ReturnError: c.ErrorCode, err = ParseIE(b[offset:]) if err != nil { @@ -460,6 +471,12 @@ func (c *Component) UnmarshalBinary(b []byte) error { return err } } + + c.SetLength() + if expectedLength != c.Length { + return fmt.Errorf("decoded Length is not equal to Component Length, got %d, expected %d", c.Length, b[1]) + } + return nil } @@ -478,14 +495,13 @@ func (c *Component) setParameterFromBytes(b []byte) error { Tag: NewUniversalConstructorTag(0x10), Value: b, } - return nil } c.Parameter = &IE{ // TODO: tag should not be determined here. Tag: NewUniversalConstructorTag(0x10), - Value: b, + Value: []byte{}, // If we were able to parse the IEs, do not set value IE: ies, } return nil diff --git a/dialogue-pdu.go b/dialogue-pdu.go index 4c097f8..268755a 100644 --- a/dialogue-pdu.go +++ b/dialogue-pdu.go @@ -386,7 +386,7 @@ func ParseDialoguePDU(b []byte) (*DialoguePDU, error) { } // UnmarshalBinary sets the values retrieved from byte sequence in an DialoguePDU. -func (d *DialoguePDU) UnmarshalBinary(b []byte) error { +func (d *DialoguePDU) UnmarshalBinary(b []byte) (err error) { if len(b) < 4 { return io.ErrUnexpectedEOF } @@ -396,14 +396,25 @@ func (d *DialoguePDU) UnmarshalBinary(b []byte) error { switch d.Type.Code() { case AARQ: - return d.parseAARQFromBytes(b) + err = d.parseAARQFromBytes(b) case AARE: - return d.parseAAREFromBytes(b) + err = d.parseAAREFromBytes(b) case ABRT: - return d.parseABRTFromBytes(b) + err = d.parseABRTFromBytes(b) default: - return &InvalidCodeError{Code: d.Type.Code()} + err = &InvalidCodeError{Code: d.Type.Code()} } + + if err != nil { + return + } + + d.SetLength() + if b[1] != d.Length { + return fmt.Errorf("decoded Length is not equal to DialoguePDU Length, got %d, expected %d", d.Length, b[1]) + } + + return nil } func (d *DialoguePDU) parseAARQFromBytes(b []byte) error { @@ -700,7 +711,7 @@ func (d *DialoguePDU) ContextVersion() string { // String returns DialoguePDU in human readable string. func (d *DialoguePDU) String() string { - return fmt.Sprintf("{Type: %#x, Length: %d, ProtocolVersion: %v, ApplicationContextName: %v, Result: %v, ResultSourceDiagnostic: %v, AbortSource: %v}", + return fmt.Sprintf("{Type: %#x, Length: %d, ProtocolVersion: %v, ApplicationContextName: %v, Result: %v, ResultSourceDiagnostic: %v, AbortSource: %v, UserInformation: %v}", d.Type, d.Length, d.ProtocolVersion, @@ -708,5 +719,6 @@ func (d *DialoguePDU) String() string { d.Result, d.ResultSourceDiagnostic, d.AbortSource, + d.UserInformation, ) } diff --git a/dialogue.go b/dialogue.go index 0af0967..a043f9d 100644 --- a/dialogue.go +++ b/dialogue.go @@ -76,21 +76,26 @@ func (d *Dialogue) MarshalTo(b []byte) error { offset += field.MarshalLen() } - if field := d.SingleAsn1Type; field != nil { - if err := field.MarshalTo(b[offset : offset+field.MarshalLen()]); err != nil { - return err - } - offset += field.MarshalLen() + if d.SingleAsn1Type == nil { + copy(b[offset:], d.Payload) + return nil } if field := d.DialoguePDU; field != nil { - if err := field.MarshalTo(b[offset : offset+field.MarshalLen()]); err != nil { + d.SingleAsn1Type.Value = make([]byte, field.MarshalLen()) + if err := field.MarshalTo(d.SingleAsn1Type.Value); err != nil { return err } - offset += field.MarshalLen() } + d.SingleAsn1Type.SetLength() + if err := d.SingleAsn1Type.MarshalTo(b[offset : offset+d.SingleAsn1Type.MarshalLen()]); err != nil { + return err + } + offset += d.SingleAsn1Type.MarshalLen() + copy(b[offset:], d.Payload) + return nil } @@ -188,19 +193,22 @@ func (d *Dialogue) MarshalLen() int { if field := d.ObjectIdentifier; field != nil { l += field.MarshalLen() } - if field := d.SingleAsn1Type; field != nil { - l += field.MarshalLen() - } if field := d.DialoguePDU; field != nil { - l += field.MarshalLen() + l += field.MarshalLen() + 2 // 2 = SingleAsn1Type IE Header } - l += len(d.Payload) - return l + return l + len(d.Payload) } // SetLength sets the length in Length field. func (d *Dialogue) SetLength() { + if d.ObjectIdentifier != nil { + d.ObjectIdentifier.SetLength() + } + if d.DialoguePDU != nil { + d.DialoguePDU.SetLength() + } + d.Length = uint8(d.MarshalLen() - 2) d.ExternalLength = uint8(d.MarshalLen() - 4) } diff --git a/ie.go b/ie.go index 6b99aaa..40b20f9 100644 --- a/ie.go +++ b/ie.go @@ -116,13 +116,24 @@ func (i *IE) MarshalBinary() ([]byte, error) { // MarshalTo puts the byte sequence in the byte array given as b. func (i *IE) MarshalTo(b []byte) error { + var offset = 2 + if len(b) < 2 { return io.ErrUnexpectedEOF } b[0] = uint8(i.Tag) b[1] = i.Length - copy(b[2:i.MarshalLen()], i.Value) + + offset += copy(b[offset:], i.Value) + + for _, childIE := range i.IE { + if err := childIE.MarshalTo(b[offset : offset+childIE.MarshalLen()]); err != nil { + return err + } + offset += childIE.MarshalLen() + } + return nil } @@ -167,6 +178,11 @@ func (i *IE) UnmarshalBinary(b []byte) error { return io.ErrUnexpectedEOF } i.Value = b[2 : 2+int(i.Length)] + + i.SetLength() + if b[1] != i.Length { + return fmt.Errorf("decoded Length is not equal to IE Length, got %d, expected %d", i.Length, b[1]) + } return nil } @@ -228,7 +244,7 @@ func (i *IE) ParseRecursive(b []byte) error { i.Tag = Tag(b[0]) i.Length = b[1] if int(i.Length)+2 > len(b) { - return nil + return io.ErrUnexpectedEOF } i.Value = b[2 : 2+int(i.Length)] @@ -238,19 +254,29 @@ func (i *IE) ParseRecursive(b []byte) error { return nil } i.IE = append(i.IE, x...) - } + i.Value = b[2+int(i.Length):] + } return nil } // MarshalLen returns the serial length of IE. func (i *IE) MarshalLen() int { - return 2 + len(i.Value) + l := 2 + for _, childIE := range i.IE { + l += childIE.MarshalLen() + } + + return l + len(i.Value) } // SetLength sets the length in Length field. func (i *IE) SetLength() { - i.Length = uint8(len(i.Value)) + for _, childIE := range i.IE { + childIE.SetLength() + } + + i.Length = uint8(i.MarshalLen() - 2) } // String returns IE in human readable string. diff --git a/tcap.go b/tcap.go index 3bd6ceb..5b2bc0a 100644 --- a/tcap.go +++ b/tcap.go @@ -114,6 +114,7 @@ func Parse(b []byte) (*TCAP, error) { if err := t.UnmarshalBinary(b); err != nil { return nil, err } + return t, nil } @@ -123,6 +124,7 @@ func (t *TCAP) UnmarshalBinary(b []byte) error { var offset = 0 t.Transaction, err = ParseTransaction(b[offset:]) + transactionLength := t.Transaction.Length if err != nil { return err } @@ -139,16 +141,29 @@ func (t *TCAP) UnmarshalBinary(b []byte) error { if len(t.Dialogue.Payload) == 0 { return nil } + // Dialogue is not a Transaction Payload + // We have to remove Dialogue from the Payload + // Or mashaling an unmarshaled message will write back two times the Dialogue + t.Transaction.Payload = t.Transaction.Payload[t.Dialogue.MarshalLen():] t.Components, err = ParseComponents(t.Dialogue.Payload) if err != nil { return err } + + // Same as above but Components inside Dialogue Payload + t.Dialogue.Payload = t.Dialogue.Payload[t.Components.MarshalLen():] case 0x6c: t.Components, err = ParseComponents(t.Transaction.Payload) if err != nil { return err } + t.Transaction.Payload = t.Transaction.Payload[t.Components.MarshalLen():] + } + + t.SetLength() + if transactionLength != t.Transaction.Length { + return fmt.Errorf("decoded Length is not equal to Transaction Length, got %d, expected %d", t.Transaction.Length, transactionLength) } return nil