| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743 |
- package wire
- import (
- "bytes"
- "io"
- "testing"
- "github.com/stretchr/testify/assert"
- )
- func TestUnmarshal(t *testing.T) {
- tests := []struct {
- name string
- prototype any
- given []byte
- want any
- wantErr error
- }{
- {
- name: "uint8",
- prototype: &struct {
- Val uint8
- }{},
- want: &struct {
- Val uint8
- }{
- Val: 100,
- },
- given: []byte{0x64},
- },
- {
- name: "uint8 with read error",
- prototype: &struct {
- Val uint8
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "uint16",
- prototype: &struct {
- Val uint16
- }{},
- want: &struct {
- Val uint16
- }{
- Val: 100,
- },
- given: []byte{0x0, 0x64},
- },
- {
- name: "uint16 with read error",
- prototype: &struct {
- Val uint16
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "uint32",
- prototype: &struct {
- Val uint32
- }{},
- want: &struct {
- Val uint32
- }{
- Val: 100,
- },
- given: []byte{0x0, 0x0, 0x0, 0x64},
- },
- {
- name: "uint32 with read error",
- prototype: &struct {
- Val uint32
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "uint64",
- prototype: &struct {
- Val uint64
- }{},
- want: &struct {
- Val uint64
- }{
- Val: 100,
- },
- given: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64},
- },
- {
- name: "uint64 with read error",
- prototype: &struct {
- Val uint64
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "string8",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint8"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint8"`
- }{
- Val: "test-value",
- },
- given: append(
- []byte{0xa}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65}...), /* str val */
- },
- {
- name: "string8 read error",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint8"`
- }{},
- given: []byte{},
- wantErr: io.EOF,
- },
- {
- name: "string16",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint16"`
- }{
- Val: "test-value",
- },
- given: append(
- []byte{0x0, 0xa}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65}...), /* str val */
- },
- {
- name: "null-terminated string16",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{
- Val: "test-value",
- },
- given: append(
- []byte{0x0, 0xb}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00}...), /* str val */
- },
- {
- name: "null-terminated string16 with len 0",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{
- Val: "",
- },
- given: []byte{0x0, 0x00}, /* len prefix */
- },
- {
- name: "null-terminated string16 without null terminator",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{
- Val: "test-value",
- },
- given: append(
- []byte{0x0, 0xa}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65}...), /* str val */
- },
- {
- name: "null-terminated string16 with null in middle",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{},
- want: &struct {
- Val string `oscar:"len_prefix=uint16,nullterm"`
- }{
- Val: "test",
- },
- given: append(
- []byte{0x0, 0xf}, /* len prefix = 15 bytes */
- []byte{0x74, 0x65, 0x73, 0x74, 0x00, 0x6d, 0x6f, 0x72, 0x65, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x21}...), /* "test\0more-data!" */
- },
- {
- name: "string16 read error",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint16"`
- }{},
- given: []byte{},
- wantErr: io.EOF,
- },
- {
- name: "unsupported string prefix type",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint128"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x0, 0xa}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65}...), /* str val */
- },
- {
- name: "string with missing len_prefix",
- prototype: &struct {
- Val string
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x0, 0xa}, /* len prefix */
- []byte{0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65}...), /* str val */
- },
- {
- name: "partial string8",
- prototype: &struct {
- Val string `oscar:"len_prefix=uint8"`
- }{},
- wantErr: io.EOF,
- given: append(
- []byte{0xa}, /* len prefix */
- []byte{}...), /* truncated payload */
- },
- {
- name: "byte slice with uint8 len_prefix",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint8"`
- }{},
- want: &struct {
- Val []byte `oscar:"len_prefix=uint8"`
- }{
- Val: []byte(`hello`),
- },
- given: append(
- []byte{0x05}, /* len prefix */
- []byte{0x68, 0x65, 0x6c, 0x6c, 0x6f}...), /* slice val */
- },
- {
- name: "slice of invalid type with uint8 len_prefix",
- prototype: &struct {
- Val []int `oscar:"len_prefix=uint8"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: []byte{0x04, 0x65, 0x6c, 0x6c, 0x6f},
- },
- {
- name: "byte slice with uint8 len_prefix with read error",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint8"`
- }{},
- wantErr: io.EOF,
- given: append(
- []byte{0x05}, /* len prefix */
- []byte{}...), /* slice val */
- },
- {
- name: "byte slice with uint8 len_prefix read error",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint8"`
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "byte slice with uint16 len_prefix",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint16"`
- }{},
- want: &struct {
- Val []byte `oscar:"len_prefix=uint16"`
- }{
- Val: []byte(`hello`),
- },
- given: append(
- []byte{0x00, 0x05}, /* len prefix */
- []byte{0x68, 0x65, 0x6c, 0x6c, 0x6f}...), /* slice val */
- },
- {
- name: "byte slice with uint16 len_prefix read error",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint16"`
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "byte slice with invalid len_prefix",
- prototype: &struct {
- Val []byte `oscar:"len_prefix=uint128"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x00, 0x05}, /* len prefix */
- []byte{0x68, 0x65, 0x6c, 0x6c, 0x6f}...), /* slice val */
- },
- {
- name: "struct slice without prefix",
- prototype: &struct {
- Val []TLV
- }{},
- want: &struct {
- Val []TLV
- }{
- Val: []TLV{
- NewTLVBE(10, uint16(1234)),
- NewTLVBE(20, uint16(1234)),
- },
- },
- given: []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2},
- },
- {
- name: "slice of unsupported type without prefix",
- prototype: &struct {
- Val []int
- }{},
- wantErr: ErrUnmarshalFailure,
- given: []byte{0x0, 0xa, 0x0, 0x2},
- },
- {
- name: "struct slice with uint8 count_prefix",
- prototype: &struct {
- Val []TLV `oscar:"count_prefix=uint8"`
- }{},
- want: &struct {
- Val []TLV `oscar:"count_prefix=uint8"`
- }{
- Val: []TLV{
- NewTLVBE(10, uint16(1234)),
- NewTLVBE(20, uint16(1234)),
- },
- },
- given: append(
- []byte{0x02}, /* count prefix */
- []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2}...), /* slice val */
- },
- {
- name: "struct slice with uint8 count_prefix and unsupported type",
- prototype: &struct {
- Val []struct {
- Val int16
- } `oscar:"count_prefix=uint8"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x02}, /* count prefix */
- []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2}...), /* slice val */
- },
- {
- name: "struct slice with uint8 count_prefix read error",
- prototype: &struct {
- Val []TLV `oscar:"count_prefix=uint8"`
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "struct slice with uint16 count_prefix",
- prototype: &struct {
- Val []TLV `oscar:"count_prefix=uint16"`
- }{},
- want: &struct {
- Val []TLV `oscar:"count_prefix=uint16"`
- }{
- Val: []TLV{
- NewTLVBE(10, uint16(1234)),
- NewTLVBE(20, uint16(1234)),
- },
- },
- given: append(
- []byte{0x0, 0x02}, /* count prefix */
- []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2}...), /* slice val */
- },
- {
- name: "struct slice with uint16 count_prefix and unsupported type",
- prototype: &struct {
- Val []struct {
- Val int16
- } `oscar:"count_prefix=uint16"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x0, 0x02}, /* count prefix */
- []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2}...), /* slice val */
- },
- {
- name: "struct slice with uint16 count_prefix read error",
- prototype: &struct {
- Val []TLV `oscar:"count_prefix=uint16"`
- }{},
- wantErr: io.EOF,
- given: []byte{},
- },
- {
- name: "struct slice with invalid count_prefix",
- prototype: &struct {
- Val []TLV `oscar:"count_prefix=uint128"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: append(
- []byte{0x0, 0x02}, /* count prefix */
- []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2}...), /* slice val */
- },
- {
- name: "struct with uint8 len_prefix",
- prototype: &struct {
- Val0 uint8
- Val1 struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint8"`
- Val4 uint16
- }{},
- want: &struct {
- Val0 uint8
- Val1 struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint8"`
- Val4 uint16
- }{
- Val0: 34,
- Val1: struct {
- Val2 uint16
- Val3 uint8
- }{
- Val2: 16,
- Val3: 10,
- },
- Val4: 32,
- },
- given: []byte{
- 0x22, // Val0
- 0x03, // Val1 struct len
- 0x00, 0x10, // Val2
- 0x0A, // Val3
- 0x00, 0x20, // Val2
- },
- },
- {
- name: "struct with uint16 len_prefix",
- prototype: &struct {
- Val0 uint8
- Val1 struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16"`
- Val4 uint16
- }{},
- want: &struct {
- Val0 uint8
- Val1 struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16"`
- Val4 uint16
- }{
- Val0: 34,
- Val1: struct {
- Val2 uint16
- Val3 uint8
- }{
- Val2: 16,
- Val3: 10,
- },
- Val4: 32,
- },
- given: []byte{
- 0x22, // Val0
- 0x00, 0x03, // Val1 struct len
- 0x00, 0x10, // Val2
- 0x0A, // Val3
- 0x00, 0x20, // Val2
- },
- },
- {
- name: "struct with uint16 len_prefix with read error",
- prototype: &struct {
- Val1 struct {
- Val2 uint16
- } `oscar:"len_prefix=uint16"`
- }{},
- given: []byte{
- 0x00, 0x10, // 16 byte len, but the body is truncated
- },
- wantErr: io.EOF,
- },
- {
- name: "struct with unknown len_prefix",
- prototype: &struct {
- Val1 struct {
- Val2 uint16
- } `oscar:"len_prefix=uint128"`
- }{},
- wantErr: ErrUnmarshalFailure,
- },
- {
- name: "optional struct has value",
- prototype: &struct {
- Val0 uint16
- Optional *struct {
- Val1 uint16
- } `oscar:"optional"`
- }{},
- want: &struct {
- Val0 uint16
- Optional *struct {
- Val1 uint16
- } `oscar:"optional"`
- }{
- Val0: 34,
- Optional: &struct {
- Val1 uint16
- }{
- Val1: 100,
- },
- },
- given: []byte{
- 0x00, 0x22, // Val0
- 0x00, 0x64, // Val1
- },
- },
- {
- name: "optional struct with value missing `optional` struct tag",
- prototype: &struct {
- Val0 uint16
- Optional *struct {
- Val1 uint16
- }
- }{},
- given: []byte{
- 0x00, 0x22, // Val0
- 0x00, 0x64, // Val1
- },
- wantErr: ErrUnmarshalFailure,
- },
- {
- name: "optional struct doesn't have value",
- prototype: &struct {
- Val0 uint16
- Optional *struct {
- Val1 uint16
- } `oscar:"optional"`
- }{},
- want: &struct {
- Val0 uint16
- Optional *struct {
- Val1 uint16
- } `oscar:"optional"`
- }{
- Val0: 34,
- Optional: nil,
- },
- given: []byte{
- 0x00, 0x22, // Val0
- },
- },
- {
- name: "optional struct followed by value throws error",
- prototype: &struct {
- Optional *struct {
- Val0 uint16
- } `oscar:"optional"`
- Val1 uint16
- }{},
- wantErr: ErrUnmarshalFailure,
- given: []byte{
- 0x00, 0x22, // Val0
- 0x00, 0x22, // Val1
- },
- },
- {
- name: "optional non-struct field throws error",
- prototype: &struct {
- Optional *uint16 `oscar:"optional"`
- }{},
- wantErr: ErrUnmarshalFailure,
- given: []byte{
- 0x00, 0x22, // Val0
- },
- },
- {
- name: "non-struct pointer value throws error",
- prototype: func() any {
- val := 10
- ptr1 := &val
- return &ptr1
- }(),
- wantErr: ErrUnmarshalFailure,
- given: []byte{
- 0x00, 0x22, // Val0
- },
- },
- {
- name: "optional struct with uint16 len_prefix and value",
- prototype: &struct {
- Val0 uint8
- Val1 *struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16,optional"`
- }{},
- want: &struct {
- Val0 uint8
- Val1 *struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16,optional"`
- }{
- Val0: 34,
- Val1: &struct {
- Val2 uint16
- Val3 uint8
- }{
- Val2: 16,
- Val3: 10,
- },
- },
- given: []byte{
- 0x22, // Val0
- 0x00, 0x03, // Val1 struct len
- 0x00, 0x10, // Val2
- 0x0A, // Val3
- 0x00, 0x20, // Val2
- },
- },
- {
- name: "optional struct with uint16 len_prefix and no value",
- prototype: &struct {
- Val0 uint8
- Val1 *struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16,optional"`
- }{},
- want: &struct {
- Val0 uint8
- Val1 *struct {
- Val2 uint16
- Val3 uint8
- } `oscar:"len_prefix=uint16,optional"`
- }{
- Val0: 34,
- Val1: nil,
- },
- given: []byte{
- 0x22, // Val0
- },
- },
- {
- name: "optional field that isn't a pointer to a struct is unsupported",
- prototype: &struct {
- Val1 *string `oscar:"optional"`
- }{},
- given: []byte{
- 0x00,
- },
- wantErr: errNonOptionalPointer,
- },
- {
- name: "byte array",
- prototype: &struct {
- Val [5]byte
- }{},
- want: &struct {
- Val [5]byte
- }{
- Val: [5]byte{'h', 'e', 'l', 'l', 'o'},
- },
- given: []byte{0x68, 0x65, 0x6c, 0x6c, 0x6f},
- },
- {
- name: "array of invalid type",
- prototype: &struct {
- Val [5]int
- }{},
- wantErr: ErrUnmarshalFailure,
- given: []byte{1, 2, 3, 4, 5},
- },
- {
- name: "struct array",
- prototype: &struct {
- Val [2]TLV
- }{},
- want: &struct {
- Val [2]TLV
- }{
- Val: [2]TLV{
- NewTLVBE(10, uint16(1234)),
- NewTLVBE(20, uint16(1234)),
- },
- },
- given: []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- r := bytes.NewBuffer(tt.given)
- err := UnmarshalBE(tt.prototype, r)
- assert.ErrorIs(t, err, tt.wantErr)
- if tt.wantErr == nil {
- assert.Equal(t, tt.want, tt.prototype)
- }
- })
- }
- }
- // TestUnmarshalLE_ICQ2003bSaveInfoEmailTLVLengthQuirk checks ICQ 2003b "save info" / META
- // CLI_SET_FULLINFO: INF_TLV_EMAIL (ICQTLVTagsEmail, 0x015E) declares length 3 but writes 4 bytes
- // on the wire; unmarshal with oscar:"quirk=icq2003b_set_fullinfo" matches iserverd tlv_chain_c::readXXX.
- func TestUnmarshalLE_ICQ2003bSaveInfoEmailTLVLengthQuirk(t *testing.T) {
- b := []byte{
- 0x5E, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, // ICQTLVTagsEmail: LE len 3, 4 bytes on wire
- 0x58, 0x02, 0x02, 0x00, 0x00, 0x00, // ICQTLVTagsNotesText (0x0258), LE len 2
- }
- var got ICQ_0x07D0_0x0C3A_DBQueryMetaReqSetFullInfo
- err := UnmarshalLE(&got, bytes.NewReader(b))
- assert.NoError(t, err)
- assert.Len(t, got.TLVList, 2)
- assert.Equal(t, ICQTLVTagsEmail, got.TLVList[0].Tag)
- assert.Equal(t, []byte{0x01, 0x00, 0x00, 0x00}, got.TLVList[0].Value)
- assert.Equal(t, ICQTLVTagsNotesText, got.TLVList[1].Tag)
- assert.Equal(t, []byte{0x00, 0x00}, got.TLVList[1].Value)
- }
- // TestUnmarshalLE_QIP2005SearchByUIN2TLVLengthQuirk checks QIP 2005 META SearchByUIN2: TLV ICQTLVTagsUIN
- // declares length 6 but only 4 bytes (UIN) follow; unmarshal with oscar:"quirk=qip_2005_search_by_uin2"
- // matches iserverd tlv.cpp rewrite for type 0x0136.
- func TestUnmarshalLE_QIP2005SearchByUIN2TLVLengthQuirk(t *testing.T) {
- b := []byte{
- 0x36, 0x01, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, // ICQTLVTagsUIN: LE len 6 (wrong), 4-byte UIN on wire
- }
- var got ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2
- err := UnmarshalLE(&got, bytes.NewReader(b))
- assert.NoError(t, err)
- assert.Len(t, got.TLVList, 1)
- assert.Equal(t, ICQTLVTagsUIN, got.TLVList[0].Tag)
- assert.Equal(t, []byte{0x01, 0x00, 0x00, 0x00}, got.TLVList[0].Value)
- }
|