From 6c7b68458206802e3a5467703739f9f8e76a93b8 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Tue, 19 Sep 2023 18:09:22 +0200 Subject: [PATCH] Enforce maximum reflection decoding depth (#18) Use a default depth of 200 --- .github/workflows/build.yml | 15 ++-- xdr/decode.go | 80 ++++++++++-------- xdr/doc.go | 38 ++++----- xdr/encode.go | 90 +++++++++++--------- xdr2/decode.go | 86 +++++++++++-------- xdr2/doc.go | 39 +++++---- xdr2/encode.go | 90 +++++++++++--------- xdr3/decode.go | 160 +++++++++++++++++++++--------------- xdr3/decode_test.go | 30 ++++++- xdr3/doc.go | 39 +++++---- xdr3/encode.go | 93 ++++++++++++--------- xdr3/error.go | 26 +++--- xdr3/internal_test.go | 2 +- xdr3/main.go | 8 +- 14 files changed, 460 insertions(+), 336 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de8dc8b..2560488 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,15 +1,20 @@ name: Go -on: [push, pull_request] + +on: + push: + branches: [master] + pull_request: + jobs: build: name: Build strategy: matrix: - go: [1.13, 1.14] + go: ["1.20", "1.21"] runs-on: ubuntu-latest - container: golang:${{ matrix.go }}-stretch + container: golang:${{ matrix.go }}-bookworm steps: - - run: go get golang.org/x/tools/cmd/goimports - - run: go get golang.org/x/lint/golint + - run: go install golang.org/x/tools/cmd/goimports@latest + - run: go install golang.org/x/lint/golint@latest - uses: actions/checkout@v1 - run: ./gotest.sh diff --git a/xdr/decode.go b/xdr/decode.go index 5eaf52d..7ea6636 100644 --- a/xdr/decode.go +++ b/xdr/decode.go @@ -61,11 +61,11 @@ by v and performs a mapping of underlying XDR types to Go types as follows: Notes and Limitations: - * Automatic unmarshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices and - byte arrays are assumed to be opaque data and byte is a Go alias for uint8 - thus indistinguishable under reflection - * Cyclic data structures are not supported and will result in infinite loops + - Automatic unmarshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices and + byte arrays are assumed to be opaque data and byte is a Go alias for uint8 + thus indistinguishable under reflection + - Cyclic data structures are not supported and will result in infinite loops If any issues are encountered during the unmarshalling process, an UnmarshalError is returned with a human readable description as well as @@ -120,8 +120,9 @@ type Decoder struct { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (d *Decoder) DecodeInt() (rv int32, err error) { data := d.data if len(data) < 4 { @@ -142,8 +143,9 @@ func (d *Decoder) DecodeInt() (rv int32, err error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (d *Decoder) DecodeUint() (rv uint32, err error) { data := d.data if len(data) < 4 { @@ -166,8 +168,9 @@ func (d *Decoder) DecodeUint() (rv uint32, err error) { // the parsed enumeration value is not one of the provided valid values. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (rv int32, err error) { val, err := d.DecodeInt() if err != nil { @@ -187,8 +190,9 @@ func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (rv int32, err error) { // the parsed value is not a 0 or 1. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (d *Decoder) DecodeBool() (rv bool, err error) { val, err := d.DecodeInt() if err != nil { @@ -210,8 +214,9 @@ func (d *Decoder) DecodeBool() (rv bool, err error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (d *Decoder) DecodeHyper() (rv int64, err error) { data := d.data if len(data) < 8 { @@ -234,8 +239,9 @@ func (d *Decoder) DecodeHyper() (rv int64, err error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (d *Decoder) DecodeUhyper() (rv uint64, err error) { data := d.data if len(data) < 8 { @@ -258,8 +264,9 @@ func (d *Decoder) DecodeUhyper() (rv uint64, err error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (d *Decoder) DecodeFloat() (rv float32, err error) { data := d.data if len(data) < 4 { @@ -280,8 +287,9 @@ func (d *Decoder) DecodeFloat() (rv float32, err error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (d *Decoder) DecodeDouble() (rv float64, err error) { data := d.data if len(data) < 8 { @@ -310,8 +318,9 @@ func (d *Decoder) DecodeDouble() (rv float64, err error) { // multiple of 4. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (d *Decoder) DecodeFixedOpaque(size int32) (rv []byte, err error) { if size == 0 { return @@ -341,8 +350,9 @@ func (d *Decoder) DecodeFixedOpaque(size int32) (rv []byte, err error) { // the opaque data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (d *Decoder) DecodeOpaque() (rv []byte, err error) { dataLen, err := d.DecodeUint() if err != nil { @@ -365,8 +375,9 @@ func (d *Decoder) DecodeOpaque() (rv []byte, err error) { // the string data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of four func (d *Decoder) DecodeString() (rv string, err error) { dataLen, err := d.DecodeUint() if err != nil { @@ -394,8 +405,9 @@ func (d *Decoder) DecodeString() (rv string, err error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (err error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless ignored. if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { @@ -428,8 +440,9 @@ func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (err erro // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array elements +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array elements func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (err error) { dataLen, err := d.DecodeUint() if err != nil { @@ -479,8 +492,9 @@ func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (err error) { // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct func (d *Decoder) decodeStruct(v reflect.Value) (err error) { vt := v.Type() for i := 0; i < v.NumField(); i++ { diff --git a/xdr/doc.go b/xdr/doc.go index b039096..4a46ccb 100644 --- a/xdr/doc.go +++ b/xdr/doc.go @@ -31,8 +31,8 @@ capabilities of Go as described below. This package provides two approaches for encoding and decoding XDR data: - 1) Marshal/Unmarshal functions which automatically map between XDR and Go types - 2) Individual Encoder/Decoder objects to manually work with XDR primitives + 1. Marshal/Unmarshal functions which automatically map between XDR and Go types + 2. Individual Encoder/Decoder objects to manually work with XDR primitives For the Marshal/Unmarshal functions, Go reflection capabilities are used to choose the type of the underlying XDR data based upon the Go type to encode or the target @@ -45,7 +45,7 @@ encode / decode the XDR data thereby eliminating the need to write a lot of boilerplate code to encode/decode and error check each piece of XDR data as is typically required with C based XDR libraries. -Go Type to XDR Type Mappings +# Go Type to XDR Type Mappings The following chart shows an overview of how Go types are mapped to XDR types for automatic marshalling and unmarshalling. The documentation for the Marshal @@ -72,21 +72,21 @@ and Unmarshal functions has specific details of how the mapping proceeds. Notes and Limitations: - * Automatic marshalling and unmarshalling of variable and fixed-length arrays - of uint8s require a special struct tag `xdropaque:"false"` since byte - slices and byte arrays are assumed to be opaque data and byte is a Go - alias for uint8 thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite loops - * Strings are marshalled and unmarshalled with UTF-8 character encoding - which differs from the XDR specification of ASCII, however UTF-8 is - backwards compatible with ASCII so this should rarely cause issues + - Automatic marshalling and unmarshalling of variable and fixed-length arrays + of uint8s require a special struct tag `xdropaque:"false"` since byte + slices and byte arrays are assumed to be opaque data and byte is a Go + alias for uint8 thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite loops + - Strings are marshalled and unmarshalled with UTF-8 character encoding + which differs from the XDR specification of ASCII, however UTF-8 is + backwards compatible with ASCII so this should rarely cause issues - -Encoding +# Encoding To encode XDR data, use the Marshal function. + func Marshal(v interface{}) (rv []byte, err error) For example, given the following code snippet: @@ -110,17 +110,16 @@ sequence: 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A - In addition, while the automatic marshalling discussed above will work for the vast majority of cases, an Encoder object is provided that can be used to manually encode XDR primitives for complex scenarios where automatic reflection-based encoding won't work. The included examples provide a sample of manual usage via an Encoder. - -Decoding +# Decoding To decode XDR data, use the Unmarshal function. + func Unmarshal(data []byte, v interface{}) (rest []byte, err error) For example, given the following code snippet: @@ -156,7 +155,7 @@ manually decode XDR primitives for complex scenarios where automatic reflection-based decoding won't work. The included examples provide a sample of manual usage via an Decoder. -Errors +# Errors All errors are either of type UnmarshalError or MarshalError. Both provide human readable output as well as an ErrorCode field which can be inspected by @@ -164,6 +163,5 @@ sophisticated callers if necessary See the documentation of UnmarshalError, MarshalError, and ErrorCode for further details. - */ package xdr diff --git a/xdr/encode.go b/xdr/encode.go index 4793dbc..9232d2c 100644 --- a/xdr/encode.go +++ b/xdr/encode.go @@ -54,16 +54,16 @@ by v and performs a mapping of Go types to the underlying XDR types as follows: Notes and Limitations: - * Automatic marshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices and - byte arrays are assumed to be opaque data and byte is a Go alias for uint8 - thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite loops - * Strings are marshalled with UTF-8 character encoding which differs from - the XDR specification of ASCII, however UTF-8 is backwards compatible with - ASCII so this should rarely cause issues + - Automatic marshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices and + byte arrays are assumed to be opaque data and byte is a Go alias for uint8 + thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite loops + - Strings are marshalled with UTF-8 character encoding which differs from + the XDR specification of ASCII, however UTF-8 is backwards compatible with + ASCII so this should rarely cause issues If any issues are encountered during the marshalling process, a MarshalError is returned with a human readable description as well as an ErrorCode value for @@ -109,8 +109,9 @@ type Encoder struct { // internal data slice. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (enc *Encoder) EncodeInt(v int32) (err error) { b := make([]byte, 4, 4) b[0] = byte(v >> 24) @@ -134,8 +135,9 @@ func (enc *Encoder) EncodeInt(v int32) (err error) { // internal data slice. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (enc *Encoder) EncodeUint(v uint32) (err error) { b := make([]byte, 4, 4) b[0] = byte(v >> 24) @@ -160,8 +162,9 @@ func (enc *Encoder) EncodeUint(v uint32) (err error) { // valid values or appending the data would overflow the internal data slice. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (err error) { if !validEnums[v] { err = marshalError("EncodeEnum", ErrBadEnumValue, "invalid enum", v) @@ -177,8 +180,9 @@ func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (err error) { // internal data slice. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (enc *Encoder) EncodeBool(v bool) (err error) { i := int32(0) if v == true { @@ -194,8 +198,9 @@ func (enc *Encoder) EncodeBool(v bool) (err error) { // internal data slice. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (enc *Encoder) EncodeHyper(v int64) (err error) { b := make([]byte, 8, 8) b[0] = byte(v >> 56) @@ -223,8 +228,9 @@ func (enc *Encoder) EncodeHyper(v int64) (err error) { // internal data slice. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (enc *Encoder) EncodeUhyper(v uint64) (err error) { b := make([]byte, 8, 8) b[0] = byte(v >> 56) @@ -252,8 +258,9 @@ func (enc *Encoder) EncodeUhyper(v uint64) (err error) { // internal data slice. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (enc *Encoder) EncodeFloat(v float32) (err error) { ui := math.Float32bits(v) return enc.EncodeUint(ui) @@ -266,8 +273,9 @@ func (enc *Encoder) EncodeFloat(v float32) (err error) { // internal data slice. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (enc *Encoder) EncodeDouble(v float64) (err error) { ui := math.Float64bits(v) return enc.EncodeUhyper(ui) @@ -284,8 +292,9 @@ func (enc *Encoder) EncodeDouble(v float64) (err error) { // internal data slice. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (enc *Encoder) EncodeFixedOpaque(v []byte) (err error) { l := len(v) pad := (4 - (l % 4)) % 4 @@ -315,8 +324,9 @@ func (enc *Encoder) EncodeFixedOpaque(v []byte) (err error) { // supports. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (enc *Encoder) EncodeOpaque(v []byte) (err error) { dataLen := len(v) if uint(dataLen) > math.MaxUint32 { @@ -341,8 +351,9 @@ func (enc *Encoder) EncodeOpaque(v []byte) (err error) { // internal data slice or the length of the string is larger than XDR supports. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of four func (enc *Encoder) EncodeString(v string) (err error) { dataLen := len(v) if uint(dataLen) > math.MaxUint32 { @@ -366,8 +377,9 @@ func (enc *Encoder) EncodeString(v string) (err error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (err error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless ignored. if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { @@ -408,8 +420,9 @@ func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (err er // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array elements +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array elements func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (err error) { numItems := uint32(v.Len()) err = enc.encode(reflect.ValueOf(numItems)) @@ -429,8 +442,9 @@ func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (err error) // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct func (enc *Encoder) encodeStruct(v reflect.Value) (err error) { vt := v.Type() for i := 0; i < v.NumField(); i++ { diff --git a/xdr2/decode.go b/xdr2/decode.go index e666a2d..4a7eca1 100644 --- a/xdr2/decode.go +++ b/xdr2/decode.go @@ -61,12 +61,12 @@ by v and performs a mapping of underlying XDR types to Go types as follows: Notes and Limitations: - * Automatic unmarshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices - and byte arrays are assumed to be opaque data and byte is a Go alias - for uint8 thus indistinguishable under reflection - * Cyclic data structures are not supported and will result in infinite - loops + - Automatic unmarshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices + and byte arrays are assumed to be opaque data and byte is a Go alias + for uint8 thus indistinguishable under reflection + - Cyclic data structures are not supported and will result in infinite + loops If any issues are encountered during the unmarshalling process, an UnmarshalError is returned with a human readable description as well as @@ -98,8 +98,9 @@ type Decoder struct { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (d *Decoder) DecodeInt() (int32, int, error) { var buf [4]byte n, err := io.ReadFull(d.r, buf[:]) @@ -120,8 +121,9 @@ func (d *Decoder) DecodeInt() (int32, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (d *Decoder) DecodeUint() (uint32, int, error) { var buf [4]byte n, err := io.ReadFull(d.r, buf[:]) @@ -145,8 +147,9 @@ func (d *Decoder) DecodeUint() (uint32, int, error) { // the parsed enumeration value is not one of the provided valid values. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) { val, n, err := d.DecodeInt() if err != nil { @@ -168,8 +171,9 @@ func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) { // the parsed value is not a 0 or 1. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (d *Decoder) DecodeBool() (bool, int, error) { val, n, err := d.DecodeInt() if err != nil { @@ -193,8 +197,9 @@ func (d *Decoder) DecodeBool() (bool, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (d *Decoder) DecodeHyper() (int64, int, error) { var buf [8]byte n, err := io.ReadFull(d.r, buf[:]) @@ -218,8 +223,9 @@ func (d *Decoder) DecodeHyper() (int64, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (d *Decoder) DecodeUhyper() (uint64, int, error) { var buf [8]byte n, err := io.ReadFull(d.r, buf[:]) @@ -242,8 +248,9 @@ func (d *Decoder) DecodeUhyper() (uint64, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (d *Decoder) DecodeFloat() (float32, int, error) { var buf [4]byte n, err := io.ReadFull(d.r, buf[:]) @@ -265,8 +272,9 @@ func (d *Decoder) DecodeFloat() (float32, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (d *Decoder) DecodeDouble() (float64, int, error) { var buf [8]byte n, err := io.ReadFull(d.r, buf[:]) @@ -296,8 +304,9 @@ func (d *Decoder) DecodeDouble() (float64, int, error) { // multiple of 4. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) { // Nothing to do if size is 0. if size == 0 { @@ -331,8 +340,9 @@ func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) { // the opaque data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (d *Decoder) DecodeOpaque() ([]byte, int, error) { dataLen, n, err := d.DecodeUint() if err != nil { @@ -363,9 +373,10 @@ func (d *Decoder) DecodeOpaque() ([]byte, int, error) { // the string data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of -// four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of +// four func (d *Decoder) DecodeString() (string, int, error) { dataLen, n, err := d.DecodeUint() if err != nil { @@ -395,8 +406,9 @@ func (d *Decoder) DecodeString() (string, int, error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless // ignored. @@ -433,9 +445,10 @@ func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, err // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array -// elements +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array +// elements func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { dataLen, n, err := d.DecodeUint() if err != nil { @@ -488,8 +501,9 @@ func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { var n int vt := v.Type() diff --git a/xdr2/doc.go b/xdr2/doc.go index 8823d62..d167f7f 100644 --- a/xdr2/doc.go +++ b/xdr2/doc.go @@ -31,8 +31,8 @@ of Go as described below. This package provides two approaches for encoding and decoding XDR data: - 1) Marshal/Unmarshal functions which automatically map between XDR and Go types - 2) Individual Encoder/Decoder objects to manually work with XDR primitives + 1. Marshal/Unmarshal functions which automatically map between XDR and Go types + 2. Individual Encoder/Decoder objects to manually work with XDR primitives For the Marshal/Unmarshal functions, Go reflection capabilities are used to choose the type of the underlying XDR data based upon the Go type to encode or @@ -45,7 +45,7 @@ automatically encode / decode the XDR data thereby eliminating the need to write a lot of boilerplate code to encode/decode and error check each piece of XDR data as is typically required with C based XDR libraries. -Go Type to XDR Type Mappings +# Go Type to XDR Type Mappings The following chart shows an overview of how Go types are mapped to XDR types for automatic marshalling and unmarshalling. The documentation for the Marshal @@ -72,22 +72,22 @@ and Unmarshal functions has specific details of how the mapping proceeds. Notes and Limitations: - * Automatic marshalling and unmarshalling of variable and fixed-length - arrays of uint8s require a special struct tag `xdropaque:"false"` - since byte slices and byte arrays are assumed to be opaque data and - byte is a Go alias for uint8 thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite - loops - * Strings are marshalled and unmarshalled with UTF-8 character encoding - which differs from the XDR specification of ASCII, however UTF-8 is - backwards compatible with ASCII so this should rarely cause issues + - Automatic marshalling and unmarshalling of variable and fixed-length + arrays of uint8s require a special struct tag `xdropaque:"false"` + since byte slices and byte arrays are assumed to be opaque data and + byte is a Go alias for uint8 thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite + loops + - Strings are marshalled and unmarshalled with UTF-8 character encoding + which differs from the XDR specification of ASCII, however UTF-8 is + backwards compatible with ASCII so this should rarely cause issues - -Encoding +# Encoding To encode XDR data, use the Marshal function. + func Marshal(w io.Writer, v interface{}) (int, error) For example, given the following code snippet: @@ -112,17 +112,16 @@ sequence: 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A - In addition, while the automatic marshalling discussed above will work for the vast majority of cases, an Encoder object is provided that can be used to manually encode XDR primitives for complex scenarios where automatic reflection-based encoding won't work. The included examples provide a sample of manual usage via an Encoder. - -Decoding +# Decoding To decode XDR data, use the Unmarshal function. + func Unmarshal(r io.Reader, v interface{}) (int, error) For example, given the following code snippet: @@ -159,7 +158,7 @@ manually decode XDR primitives for complex scenarios where automatic reflection-based decoding won't work. The included examples provide a sample of manual usage via a Decoder. -Errors +# Errors All errors are either of type UnmarshalError or MarshalError. Both provide human-readable output as well as an ErrorCode field which can be inspected by diff --git a/xdr2/encode.go b/xdr2/encode.go index 7bac268..1137539 100644 --- a/xdr2/encode.go +++ b/xdr2/encode.go @@ -55,16 +55,16 @@ v and performs a mapping of Go types to the underlying XDR types as follows: Notes and Limitations: - * Automatic marshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices and - byte arrays are assumed to be opaque data and byte is a Go alias for uint8 - thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite loops - * Strings are marshalled with UTF-8 character encoding which differs from - the XDR specification of ASCII, however UTF-8 is backwards compatible with - ASCII so this should rarely cause issues + - Automatic marshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices and + byte arrays are assumed to be opaque data and byte is a Go alias for uint8 + thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite loops + - Strings are marshalled with UTF-8 character encoding which differs from + the XDR specification of ASCII, however UTF-8 is backwards compatible with + ASCII so this should rarely cause issues If any issues are encountered during the marshalling process, a MarshalError is returned with a human readable description as well as an ErrorCode value for @@ -90,8 +90,9 @@ type Encoder struct { // fails. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (enc *Encoder) EncodeInt(v int32) (int, error) { var b [4]byte b[0] = byte(v >> 24) @@ -117,8 +118,9 @@ func (enc *Encoder) EncodeInt(v int32) (int, error) { // fails. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (enc *Encoder) EncodeUint(v uint32) (int, error) { var b [4]byte b[0] = byte(v >> 24) @@ -145,8 +147,9 @@ func (enc *Encoder) EncodeUint(v uint32) (int, error) { // provided valid values or if writing the data fails. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) { if !validEnums[v] { err := marshalError("EncodeEnum", ErrBadEnumValue, @@ -163,8 +166,9 @@ func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) // fails. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (enc *Encoder) EncodeBool(v bool) (int, error) { i := int32(0) if v == true { @@ -181,8 +185,9 @@ func (enc *Encoder) EncodeBool(v bool) (int, error) { // fails. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (enc *Encoder) EncodeHyper(v int64) (int, error) { var b [8]byte b[0] = byte(v >> 56) @@ -212,8 +217,9 @@ func (enc *Encoder) EncodeHyper(v int64) (int, error) { // fails. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (enc *Encoder) EncodeUhyper(v uint64) (int, error) { var b [8]byte b[0] = byte(v >> 56) @@ -243,8 +249,9 @@ func (enc *Encoder) EncodeUhyper(v uint64) (int, error) { // fails. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (enc *Encoder) EncodeFloat(v float32) (int, error) { ui := math.Float32bits(v) return enc.EncodeUint(ui) @@ -258,8 +265,9 @@ func (enc *Encoder) EncodeFloat(v float32) (int, error) { // fails. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (enc *Encoder) EncodeDouble(v float64) (int, error) { ui := math.Float64bits(v) return enc.EncodeUhyper(ui) @@ -277,8 +285,9 @@ func (enc *Encoder) EncodeDouble(v float64) (int, error) { // fails. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) { l := len(v) pad := (4 - (l % 4)) % 4 @@ -318,8 +327,9 @@ func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) { // fails. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (enc *Encoder) EncodeOpaque(v []byte) (int, error) { // Length of opaque data. n, err := enc.EncodeUint(uint32(len(v))) @@ -343,8 +353,9 @@ func (enc *Encoder) EncodeOpaque(v []byte) (int, error) { // fails. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of four func (enc *Encoder) EncodeString(v string) (int, error) { // Length of string. n, err := enc.EncodeUint(uint32(len(v))) @@ -367,8 +378,9 @@ func (enc *Encoder) EncodeString(v string) (int, error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless ignored. if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { @@ -412,8 +424,9 @@ func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, e // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array elements +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array elements func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { numItems := uint32(v.Len()) n, err := enc.EncodeUint(numItems) @@ -436,8 +449,9 @@ func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { var n int vt := v.Type() diff --git a/xdr3/decode.go b/xdr3/decode.go index 7608af6..cf97e9e 100644 --- a/xdr3/decode.go +++ b/xdr3/decode.go @@ -30,6 +30,9 @@ const maxInt32 = int(^uint32(0) >> 1) var errMaxSlice = "data exceeds max slice limit" var errIODecode = "%s while decoding %d bytes" +// DecodeDefaultMaxDepth is the default maximum decoding depth +const DecodeDefaultMaxDepth = 200 + /* Unmarshal parses XDR-encoded data into the value pointed to by v reading from reader r and returning the total number of bytes read. An addressable pointer @@ -62,12 +65,11 @@ by v and performs a mapping of underlying XDR types to Go types as follows: Notes and Limitations: - * Automatic unmarshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices - and byte arrays are assumed to be opaque data and byte is a Go alias - for uint8 thus indistinguishable under reflection - * Cyclic data structures are not supported and will result in infinite - loops + - Automatic unmarshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices + and byte arrays are assumed to be opaque data and byte is a Go alias + for uint8 thus indistinguishable under reflection + - Cyclic data structures are not supported and will result in ErrMaxDecodingDepth errors If any issues are encountered during the unmarshalling process, an UnmarshalError is returned with a human readable description as well as @@ -101,8 +103,9 @@ type Decoder struct { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (d *Decoder) DecodeInt() (int32, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:4]) if err != nil { @@ -122,8 +125,9 @@ func (d *Decoder) DecodeInt() (int32, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (d *Decoder) DecodeUint() (uint32, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:4]) if err != nil { @@ -146,8 +150,9 @@ func (d *Decoder) DecodeUint() (uint32, int, error) { // the parsed enumeration value is not one of the provided valid values. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) { val, n, err := d.DecodeInt() if err != nil { @@ -169,8 +174,9 @@ func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) { // the parsed value is not a 0 or 1. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (d *Decoder) DecodeBool() (bool, int, error) { val, n, err := d.DecodeInt() if err != nil { @@ -194,8 +200,9 @@ func (d *Decoder) DecodeBool() (bool, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (d *Decoder) DecodeHyper() (int64, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:8]) if err != nil { @@ -218,8 +225,9 @@ func (d *Decoder) DecodeHyper() (int64, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (d *Decoder) DecodeUhyper() (uint64, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:8]) if err != nil { @@ -241,8 +249,9 @@ func (d *Decoder) DecodeUhyper() (uint64, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (d *Decoder) DecodeFloat() (float32, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:4]) if err != nil { @@ -263,8 +272,9 @@ func (d *Decoder) DecodeFloat() (float32, int, error) { // An UnmarshalError is returned if there are insufficient bytes remaining. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (d *Decoder) DecodeDouble() (float64, int, error) { n, err := io.ReadFull(d.r, d.scratchBuf[:8]) if err != nil { @@ -293,8 +303,9 @@ func (d *Decoder) DecodeDouble() (float64, int, error) { // multiple of 4. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) { out := make([]byte, size) n, err := d.DecodeFixedOpaqueInplace(out) @@ -363,8 +374,9 @@ func (d *Decoder) DecodeFixedOpaqueInplace(out []byte) (int, error) { // the opaque data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (d *Decoder) DecodeOpaque(maxSize int) ([]byte, int, error) { dataLen, n, err := d.DecodeUint() if err != nil { @@ -400,9 +412,10 @@ func (d *Decoder) DecodeOpaque(maxSize int) ([]byte, int, error) { // the string data is larger than the max length of a Go slice. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of -// four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of +// four func (d *Decoder) DecodeString(maxSize int) (string, int, error) { dataLen, n, err := d.DecodeUint() if err != nil { @@ -437,9 +450,10 @@ func (d *Decoder) DecodeString(maxSize int) (string, int, error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements -func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements +func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool, maxDepth uint) (int, error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless // ignored. if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { @@ -450,7 +464,7 @@ func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, err // Decode each array element. var n int for i := 0; i < v.Len(); i++ { - n2, err := d.decode(v.Index(i), 0) + n2, err := d.decode(v.Index(i), 0, maxDepth) n += n2 if err != nil { return n, err @@ -471,10 +485,11 @@ func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, err // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array -// elements -func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool, maxSize int) (int, error) { +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array +// elements +func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool, maxSize int, maxDepth uint) (int, error) { dataLen, n, err := d.DecodeUint() if err != nil { return n, err @@ -511,7 +526,7 @@ func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool, maxSize int) ( // Decode each slice element. for i := 0; i < sliceLen; i++ { - n2, err := d.decode(v.Index(i), 0) + n2, err := d.decode(v.Index(i), 0, maxDepth) n += n2 if err != nil { return n, err @@ -531,7 +546,7 @@ func setUnionArmsToNil(v reflect.Value) { } // decodeUnion -func (d *Decoder) decodeUnion(v reflect.Value) (int, error) { +func (d *Decoder) decodeUnion(v reflect.Value, maxDepth uint) (int, error) { // we should have already checked that v is a union // prior to this call, so we panic if v is not a union u := v.Interface().(Union) @@ -595,7 +610,7 @@ func (d *Decoder) decodeUnion(v reflect.Value) (int, error) { maxSize = int(sz) } - n2, err := d.decode(vv.Elem(), maxSize) + n2, err := d.decode(vv.Elem(), maxSize, maxDepth) n += n2 if err != nil { @@ -613,9 +628,10 @@ func (d *Decoder) decodeUnion(v reflect.Value) (int, error) { // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct -func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct +func (d *Decoder) decodeStruct(v reflect.Value, maxDepth uint) (int, error) { var n int vt := v.Type() for i := 0; i < v.NumField(); i++ { @@ -648,7 +664,7 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { maxSize = dest.XDRMaxSize() } - n2, err := d.decodeArray(vf, true, maxSize) + n2, err := d.decodeArray(vf, true, maxSize, maxDepth) n += n2 if err != nil { return n, err @@ -656,7 +672,7 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { continue case reflect.Array: - n2, err := d.decodeFixedArray(vf, true) + n2, err := d.decodeFixedArray(vf, true, maxDepth) n += n2 if err != nil { return n, err @@ -676,7 +692,7 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { } // Decode each struct field. - n2, err := d.decode(vf, maxSize) + n2, err := d.decode(vf, maxSize, maxDepth) n += n2 if err != nil { return n, err @@ -703,7 +719,7 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { // // An UnmarshalError is returned if any issues are encountered while decoding // the elements. -func (d *Decoder) decodeMap(v reflect.Value) (int, error) { +func (d *Decoder) decodeMap(v reflect.Value, maxDepth uint) (int, error) { dataLen, n, err := d.DecodeUint() if err != nil { return n, err @@ -720,14 +736,14 @@ func (d *Decoder) decodeMap(v reflect.Value) (int, error) { elemType := vt.Elem() for i := uint32(0); i < dataLen; i++ { key := reflect.New(keyType).Elem() - n2, err := d.decode(key, 0) + n2, err := d.decode(key, 0, maxDepth) n += n2 if err != nil { return n, err } val := reflect.New(elemType).Elem() - n2, err = d.decode(val, 0) + n2, err = d.decode(val, 0, maxDepth) n += n2 if err != nil { return n, err @@ -745,7 +761,7 @@ func (d *Decoder) decodeMap(v reflect.Value) (int, error) { // // An UnmarshalError is returned if any issues are encountered while decoding // the interface. -func (d *Decoder) decodeInterface(v reflect.Value) (int, error) { +func (d *Decoder) decodeInterface(v reflect.Value, maxDepth uint) (int, error) { if v.IsNil() || !v.CanInterface() { msg := fmt.Sprintf("can't decode to nil interface") err := unmarshalError("decodeInterface", ErrNilInterface, msg, @@ -767,15 +783,20 @@ func (d *Decoder) decodeInterface(v reflect.Value) (int, error) { nil, nil) return 0, err } - return d.decode(ve, 0) + return d.decode(ve, 0, maxDepth) } // decode is the main workhorse for unmarshalling via reflection. It uses // the passed reflection value to choose the XDR primitives to decode from // the encapsulated reader. It is a recursive function, -// so cyclic data structures are not supported and will result in an infinite -// loop. It returns the the number of bytes actually read. -func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { +// so cyclic data structures are not supported and will result in an ErrMaxDecodingDepth +// error. It returns the number of bytes actually read. +func (d *Decoder) decode(ve reflect.Value, maxSize int, maxDepth uint) (int, error) { + if maxDepth == 0 { + return 0, unmarshalError("decode", ErrMaxDecodingDepth, "maximum decoding depth reached", nil, nil) + } + maxDepth-- + if !ve.IsValid() { msg := fmt.Sprintf("type '%s' is not valid", ve.Kind().String()) err := unmarshalError("decode", ErrUnsupportedType, msg, nil, nil) @@ -784,7 +805,7 @@ func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { // Handle time.Time values by decoding them as an RFC3339 formatted // string with nanosecond precision. Check the type string rather - // than doing a full blown conversion to interface and type assertion + // than doing a full-blown conversion to interface and type assertion // since checking a string is much quicker. if ve.Type().String() == "time.Time" { // Read the value as a string and parse it. @@ -806,7 +827,7 @@ func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { switch ve.Kind() { case reflect.Ptr: - return d.decodePtr(ve) + return d.decodePtr(ve, maxDepth) case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int: i, n, err := d.DecodeInt() @@ -898,7 +919,7 @@ func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { return n, nil case reflect.Array: - n, err := d.decodeFixedArray(ve, false) + n, err := d.decodeFixedArray(ve, false, maxDepth) if err != nil { return n, err } @@ -909,7 +930,7 @@ func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { maxSize = dest.XDRMaxSize() } - n, err := d.decodeArray(ve, false, maxSize) + n, err := d.decodeArray(ve, false, maxSize, maxDepth) if err != nil { return n, err } @@ -920,24 +941,24 @@ func (d *Decoder) decode(ve reflect.Value, maxSize int) (int, error) { // we need to init the union's value interface if _, ok := ve.Interface().(Union); ok { - return d.decodeUnion(ve) + return d.decodeUnion(ve, maxDepth) } - n, err := d.decodeStruct(ve) + n, err := d.decodeStruct(ve, maxDepth) if err != nil { return n, err } return n, nil case reflect.Map: - n, err := d.decodeMap(ve) + n, err := d.decodeMap(ve, maxDepth) if err != nil { return n, err } return n, nil case reflect.Interface: - n, err := d.decodeInterface(ve) + n, err := d.decodeInterface(ve, maxDepth) if err != nil { return n, err } @@ -996,7 +1017,7 @@ func allocPtrIfNil(v *reflect.Value) error { // decodePtr decodes a single tagged XDR pointer type: one 4-byte // boolean followed by an encoded referent, which is allocated if needed. -func (d *Decoder) decodePtr(v reflect.Value) (int, error) { +func (d *Decoder) decodePtr(v reflect.Value, maxDepth uint) (int, error) { present, n, err := d.DecodeBool() @@ -1013,7 +1034,7 @@ func (d *Decoder) decodePtr(v reflect.Value) (int, error) { return n, err } - n2, err := d.decode(v.Elem(), 0) + n2, err := d.decode(v.Elem(), 0, maxDepth) return n + n2, err } @@ -1029,9 +1050,14 @@ func (d *Decoder) indirectIfPtr(v reflect.Value) (reflect.Value, error) { // Decode operates identically to the Unmarshal function with the exception of // using the reader associated with the Decoder as the source of XDR-encoded -// data instead of a user-supplied reader. See the Unmarhsal documentation for -// specifics. +// data instead of a user-supplied reader. See the Unmarhsal documentation for +// specifics. Decode(v) is equivalent to DecodeWithMaxDepth(v, DecodeDefaultMaxDepth) func (d *Decoder) Decode(v interface{}) (int, error) { + return d.DecodeWithMaxDepth(v, DecodeDefaultMaxDepth) +} + +// DecodeWithMaxDepth behaves like Decode, except an explicit maximum decoding depth is used +func (d *Decoder) DecodeWithMaxDepth(v interface{}, maxDepth uint) (int, error) { if v == nil { msg := "can't unmarshal to nil interface" return 0, unmarshalError("Unmarshal", ErrNilInterface, msg, nil, @@ -1052,7 +1078,7 @@ func (d *Decoder) Decode(v interface{}) (int, error) { return 0, err } - return d.decode(vv.Elem(), 0) + return d.decode(vv.Elem(), 0, maxDepth) } // NewDecoder returns a Decoder that can be used to manually decode XDR data diff --git a/xdr3/decode_test.go b/xdr3/decode_test.go index bc575f6..d215c35 100644 --- a/xdr3/decode_test.go +++ b/xdr3/decode_test.go @@ -745,7 +745,7 @@ func TestUnmarshalCorners(t *testing.T) { testName = "Decode to unsettable pointer" expectedN = 4 expectedErr = &UnmarshalError{ErrorCode: ErrNotSettable} - n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(i32p), 0) + n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(i32p), 0, DecodeDefaultMaxDepth) testExpectedURet(t, testName, n, expectedN, err, expectedErr) // Ensure unmarshal to indirected unsettable pointer returns the @@ -787,7 +787,7 @@ func TestUnmarshalCorners(t *testing.T) { testName = "Decode invalid reflect value" expectedN = 0 expectedErr = error(&UnmarshalError{ErrorCode: ErrUnsupportedType}) - n, err = TstDecode(bytes.NewReader(buf))(reflect.Value{}, 0) + n, err = TstDecode(bytes.NewReader(buf))(reflect.Value{}, 0, DecodeDefaultMaxDepth) testExpectedURet(t, testName, n, expectedN, err, expectedErr) // Ensure unmarshal to a slice with a cap and 0 length adjusts the @@ -847,7 +847,7 @@ func TestUnmarshalCorners(t *testing.T) { var ustruct unsettableStruct expectedN = 0 expectedErr = error(&UnmarshalError{ErrorCode: ErrNotSettable}) - n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(ustruct), 0) + n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(ustruct), 0, DecodeDefaultMaxDepth) testExpectedURet(t, testName, n, expectedN, err, expectedErr) // Ensure decode to struct with unsettable pointer fields return @@ -859,7 +859,7 @@ func TestUnmarshalCorners(t *testing.T) { var upstruct unsettablePointerStruct expectedN = 0 expectedErr = error(&UnmarshalError{ErrorCode: ErrNotSettable}) - n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(upstruct), 0) + n, err = TstDecode(bytes.NewReader(buf))(reflect.ValueOf(upstruct), 0, DecodeDefaultMaxDepth) testExpectedURet(t, testName, n, expectedN, err, expectedErr) } @@ -1133,3 +1133,25 @@ func TestDecodeUnionIntoExistingObject(t *testing.T) { t.Error("Text does not match") } } + +func TestDecodeMaxDepth(t *testing.T) { + var buf bytes.Buffer + data := "data" + _, err := Marshal(&buf, structWithPointer{Data: &data}) + if err != nil { + t.Error("unexpected error") + } + + bufCopy := buf + decoder := NewDecoder(&bufCopy) + var s structWithPointer + _, err = decoder.DecodeWithMaxDepth(&s, 3) + if err != nil { + t.Error("unexpected error") + } + + bufCopy = buf + decoder = NewDecoder(&bufCopy) + _, err = decoder.DecodeWithMaxDepth(&s, 2) + assertError(t, "", err, &UnmarshalError{ErrorCode: ErrMaxDecodingDepth}) +} diff --git a/xdr3/doc.go b/xdr3/doc.go index 8823d62..d167f7f 100644 --- a/xdr3/doc.go +++ b/xdr3/doc.go @@ -31,8 +31,8 @@ of Go as described below. This package provides two approaches for encoding and decoding XDR data: - 1) Marshal/Unmarshal functions which automatically map between XDR and Go types - 2) Individual Encoder/Decoder objects to manually work with XDR primitives + 1. Marshal/Unmarshal functions which automatically map between XDR and Go types + 2. Individual Encoder/Decoder objects to manually work with XDR primitives For the Marshal/Unmarshal functions, Go reflection capabilities are used to choose the type of the underlying XDR data based upon the Go type to encode or @@ -45,7 +45,7 @@ automatically encode / decode the XDR data thereby eliminating the need to write a lot of boilerplate code to encode/decode and error check each piece of XDR data as is typically required with C based XDR libraries. -Go Type to XDR Type Mappings +# Go Type to XDR Type Mappings The following chart shows an overview of how Go types are mapped to XDR types for automatic marshalling and unmarshalling. The documentation for the Marshal @@ -72,22 +72,22 @@ and Unmarshal functions has specific details of how the mapping proceeds. Notes and Limitations: - * Automatic marshalling and unmarshalling of variable and fixed-length - arrays of uint8s require a special struct tag `xdropaque:"false"` - since byte slices and byte arrays are assumed to be opaque data and - byte is a Go alias for uint8 thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite - loops - * Strings are marshalled and unmarshalled with UTF-8 character encoding - which differs from the XDR specification of ASCII, however UTF-8 is - backwards compatible with ASCII so this should rarely cause issues + - Automatic marshalling and unmarshalling of variable and fixed-length + arrays of uint8s require a special struct tag `xdropaque:"false"` + since byte slices and byte arrays are assumed to be opaque data and + byte is a Go alias for uint8 thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite + loops + - Strings are marshalled and unmarshalled with UTF-8 character encoding + which differs from the XDR specification of ASCII, however UTF-8 is + backwards compatible with ASCII so this should rarely cause issues - -Encoding +# Encoding To encode XDR data, use the Marshal function. + func Marshal(w io.Writer, v interface{}) (int, error) For example, given the following code snippet: @@ -112,17 +112,16 @@ sequence: 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A - In addition, while the automatic marshalling discussed above will work for the vast majority of cases, an Encoder object is provided that can be used to manually encode XDR primitives for complex scenarios where automatic reflection-based encoding won't work. The included examples provide a sample of manual usage via an Encoder. - -Decoding +# Decoding To decode XDR data, use the Unmarshal function. + func Unmarshal(r io.Reader, v interface{}) (int, error) For example, given the following code snippet: @@ -159,7 +158,7 @@ manually decode XDR primitives for complex scenarios where automatic reflection-based decoding won't work. The included examples provide a sample of manual usage via a Decoder. -Errors +# Errors All errors are either of type UnmarshalError or MarshalError. Both provide human-readable output as well as an ErrorCode field which can be inspected by diff --git a/xdr3/encode.go b/xdr3/encode.go index 32a67cc..2e0cf5b 100644 --- a/xdr3/encode.go +++ b/xdr3/encode.go @@ -55,16 +55,16 @@ v and performs a mapping of Go types to the underlying XDR types as follows: Notes and Limitations: - * Automatic marshalling of variable and fixed-length arrays of uint8s - requires a special struct tag `xdropaque:"false"` since byte slices and - byte arrays are assumed to be opaque data and byte is a Go alias for uint8 - thus indistinguishable under reflection - * Channel, complex, and function types cannot be encoded - * Interfaces without a concrete value cannot be encoded - * Cyclic data structures are not supported and will result in infinite loops - * Strings are marshalled with UTF-8 character encoding which differs from - the XDR specification of ASCII, however UTF-8 is backwards compatible with - ASCII so this should rarely cause issues + - Automatic marshalling of variable and fixed-length arrays of uint8s + requires a special struct tag `xdropaque:"false"` since byte slices and + byte arrays are assumed to be opaque data and byte is a Go alias for uint8 + thus indistinguishable under reflection + - Channel, complex, and function types cannot be encoded + - Interfaces without a concrete value cannot be encoded + - Cyclic data structures are not supported and will result in infinite loops + - Strings are marshalled with UTF-8 character encoding which differs from + the XDR specification of ASCII, however UTF-8 is backwards compatible with + ASCII so this should rarely cause issues If any issues are encountered during the marshalling process, a MarshalError is returned with a human readable description as well as an ErrorCode value for @@ -92,8 +92,9 @@ type Encoder struct { // fails. // // Reference: -// RFC Section 4.1 - Integer -// 32-bit big-endian signed integer in range [-2147483648, 2147483647] +// +// RFC Section 4.1 - Integer +// 32-bit big-endian signed integer in range [-2147483648, 2147483647] func (enc *Encoder) EncodeInt(v int32) (int, error) { enc.scratchBuf[0] = byte(v >> 24) enc.scratchBuf[1] = byte(v >> 16) @@ -118,8 +119,9 @@ func (enc *Encoder) EncodeInt(v int32) (int, error) { // fails. // // Reference: -// RFC Section 4.2 - Unsigned Integer -// 32-bit big-endian unsigned integer in range [0, 4294967295] +// +// RFC Section 4.2 - Unsigned Integer +// 32-bit big-endian unsigned integer in range [0, 4294967295] func (enc *Encoder) EncodeUint(v uint32) (int, error) { enc.scratchBuf[0] = byte(v >> 24) enc.scratchBuf[1] = byte(v >> 16) @@ -145,8 +147,9 @@ func (enc *Encoder) EncodeUint(v uint32) (int, error) { // provided valid values or if writing the data fails. // // Reference: -// RFC Section 4.3 - Enumeration -// Represented as an XDR encoded signed integer +// +// RFC Section 4.3 - Enumeration +// Represented as an XDR encoded signed integer func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) { if !validEnums[v] { err := marshalError("EncodeEnum", ErrBadEnumValue, @@ -163,8 +166,9 @@ func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) // fails. // // Reference: -// RFC Section 4.4 - Boolean -// Represented as an XDR encoded enumeration where 0 is false and 1 is true +// +// RFC Section 4.4 - Boolean +// Represented as an XDR encoded enumeration where 0 is false and 1 is true func (enc *Encoder) EncodeBool(v bool) (int, error) { i := int32(0) if v == true { @@ -181,8 +185,9 @@ func (enc *Encoder) EncodeBool(v bool) (int, error) { // fails. // // Reference: -// RFC Section 4.5 - Hyper Integer -// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] +// +// RFC Section 4.5 - Hyper Integer +// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807] func (enc *Encoder) EncodeHyper(v int64) (int, error) { enc.scratchBuf[0] = byte(v >> 56) enc.scratchBuf[1] = byte(v >> 48) @@ -211,8 +216,9 @@ func (enc *Encoder) EncodeHyper(v int64) (int, error) { // fails. // // Reference: -// RFC Section 4.5 - Unsigned Hyper Integer -// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] +// +// RFC Section 4.5 - Unsigned Hyper Integer +// 64-bit big-endian unsigned integer in range [0, 18446744073709551615] func (enc *Encoder) EncodeUhyper(v uint64) (int, error) { enc.scratchBuf[0] = byte(v >> 56) enc.scratchBuf[1] = byte(v >> 48) @@ -241,8 +247,9 @@ func (enc *Encoder) EncodeUhyper(v uint64) (int, error) { // fails. // // Reference: -// RFC Section 4.6 - Floating Point -// 32-bit single-precision IEEE 754 floating point +// +// RFC Section 4.6 - Floating Point +// 32-bit single-precision IEEE 754 floating point func (enc *Encoder) EncodeFloat(v float32) (int, error) { ui := math.Float32bits(v) return enc.EncodeUint(ui) @@ -256,8 +263,9 @@ func (enc *Encoder) EncodeFloat(v float32) (int, error) { // fails. // // Reference: -// RFC Section 4.7 - Double-Precision Floating Point -// 64-bit double-precision IEEE 754 floating point +// +// RFC Section 4.7 - Double-Precision Floating Point +// 64-bit double-precision IEEE 754 floating point func (enc *Encoder) EncodeDouble(v float64) (int, error) { ui := math.Float64bits(v) return enc.EncodeUhyper(ui) @@ -275,8 +283,9 @@ func (enc *Encoder) EncodeDouble(v float64) (int, error) { // fails. // // Reference: -// RFC Section 4.9 - Fixed-Length Opaque Data -// Fixed-length uninterpreted data zero-padded to a multiple of four +// +// RFC Section 4.9 - Fixed-Length Opaque Data +// Fixed-length uninterpreted data zero-padded to a multiple of four func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) { l := len(v) pad := (4 - (l % 4)) % 4 @@ -321,8 +330,9 @@ func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) { // fails. // // Reference: -// RFC Section 4.10 - Variable-Length Opaque Data -// Unsigned integer length followed by fixed opaque data of that length +// +// RFC Section 4.10 - Variable-Length Opaque Data +// Unsigned integer length followed by fixed opaque data of that length func (enc *Encoder) EncodeOpaque(v []byte) (int, error) { // Length of opaque data. n, err := enc.EncodeUint(uint32(len(v))) @@ -346,8 +356,9 @@ func (enc *Encoder) EncodeOpaque(v []byte) (int, error) { // fails. // // Reference: -// RFC Section 4.11 - String -// Unsigned integer length followed by bytes zero-padded to a multiple of four +// +// RFC Section 4.11 - String +// Unsigned integer length followed by bytes zero-padded to a multiple of four func (enc *Encoder) EncodeString(v string) (int, error) { // Length of string. n, err := enc.EncodeUint(uint32(len(v))) @@ -370,8 +381,9 @@ func (enc *Encoder) EncodeString(v string) (int, error) { // the array elements. // // Reference: -// RFC Section 4.12 - Fixed-Length Array -// Individually XDR encoded array elements +// +// RFC Section 4.12 - Fixed-Length Array +// Individually XDR encoded array elements func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) { // Treat [#]byte (byte is alias for uint8) as opaque data unless ignored. if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 { @@ -415,8 +427,9 @@ func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, e // the array elements. // // Reference: -// RFC Section 4.13 - Variable-Length Array -// Unsigned integer length followed by individually XDR encoded array elements +// +// RFC Section 4.13 - Variable-Length Array +// Unsigned integer length followed by individually XDR encoded array elements func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { numItems := uint32(v.Len()) n, err := enc.EncodeUint(numItems) @@ -438,7 +451,8 @@ func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) // the union. // // Reference: -// RFC Section 4.15 - Discriminated Union +// +// RFC Section 4.15 - Discriminated Union func (enc *Encoder) encodeUnion(v reflect.Value) (int, error) { // we should have already checked that v is a union // prior to this call, so we panic if v is not a union @@ -501,8 +515,9 @@ func (enc *Encoder) encodeUnion(v reflect.Value) (int, error) { // the elements. // // Reference: -// RFC Section 4.14 - Structure -// XDR encoded elements in the order of their declaration in the struct +// +// RFC Section 4.14 - Structure +// XDR encoded elements in the order of their declaration in the struct func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { var n int vt := v.Type() diff --git a/xdr3/error.go b/xdr3/error.go index 53ffbd5..ea5503a 100644 --- a/xdr3/error.go +++ b/xdr3/error.go @@ -70,20 +70,24 @@ const ( // ErrBadUnionValue indicates a union's value is not populated when it should // be ErrBadUnionValue + + // ErrMaxDecodingDepth indicates that the maximum decoding depth was reached + ErrMaxDecodingDepth ) // Map of ErrorCode values back to their constant names for pretty printing. var errorCodeStrings = map[ErrorCode]string{ - ErrBadArguments: "ErrBadArguments", - ErrUnsupportedType: "ErrUnsupportedType", - ErrBadEnumValue: "ErrBadEnumValue", - ErrNotSettable: "ErrNotSettable", - ErrOverflow: "ErrOverflow", - ErrNilInterface: "ErrNilInterface", - ErrIO: "ErrIO", - ErrParseTime: "ErrParseTime", - ErrBadUnionSwitch: "ErrBadUnionSwitch", - ErrBadUnionValue: "ErrBadUnionValue", + ErrBadArguments: "ErrBadArguments", + ErrUnsupportedType: "ErrUnsupportedType", + ErrBadEnumValue: "ErrBadEnumValue", + ErrNotSettable: "ErrNotSettable", + ErrOverflow: "ErrOverflow", + ErrNilInterface: "ErrNilInterface", + ErrIO: "ErrIO", + ErrParseTime: "ErrParseTime", + ErrBadUnionSwitch: "ErrBadUnionSwitch", + ErrBadUnionValue: "ErrBadUnionValue", + ErrMaxDecodingDepth: "ErrMaxDecodingDepth", } // String returns the ErrorCode as a human-readable name. @@ -122,7 +126,7 @@ func (e *UnmarshalError) Error() string { } // unmarshalError creates an error given a set of arguments and will copy byte -// slices into the Value field since they might otherwise be changed from from +// slices into the Value field since they might otherwise be changed from // the original value. func unmarshalError(f string, c ErrorCode, desc string, v interface{}, err error) *UnmarshalError { e := &UnmarshalError{ErrorCode: c, Func: f, Description: desc, Err: err} diff --git a/xdr3/internal_test.go b/xdr3/internal_test.go index 858b3e3..6581d72 100644 --- a/xdr3/internal_test.go +++ b/xdr3/internal_test.go @@ -37,7 +37,7 @@ func TstEncode(w io.Writer) func(v reflect.Value) (int, error) { // TstDecode creates a new Decoder for the passed reader and returns the // internal decode function on the Decoder. -func TstDecode(r io.Reader) func(v reflect.Value, maxSize int) (int, error) { +func TstDecode(r io.Reader) func(v reflect.Value, maxSize int, maxDepth uint) (int, error) { dec := NewDecoder(r) return dec.decode } diff --git a/xdr3/main.go b/xdr3/main.go index 06cba71..5ed96df 100644 --- a/xdr3/main.go +++ b/xdr3/main.go @@ -20,10 +20,10 @@ type Sized interface { // union's disciminant, whose name must be returned by ArmForSwitch(), and // one per potential value of the union, which must be a pointer. For example: // -// type Result struct { -// Type ResultType // this is the union's disciminant, may be 0 to indicate success, 1 to indicate error -// Msg *string // this field will be populated when Type == 1 -// } +// type Result struct { +// Type ResultType // this is the union's disciminant, may be 0 to indicate success, 1 to indicate error +// Msg *string // this field will be populated when Type == 1 +// } type Union interface { ArmForSwitch(int32) (string, bool) SwitchFieldName() string