mirror of
https://github.com/jfjallid/go-smb
synced 2026-06-08 15:02:50 +00:00
770 lines
21 KiB
Go
770 lines
21 KiB
Go
// MIT License
|
|
//
|
|
// Copyright (c) 2017 stacktitan
|
|
// Copyright (c) 2023 Jimmy Fjällid for contributions to support more structures
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
/*
|
|
The original encoder package implemented only the minimal viable encoder/decoder
|
|
|
|
to support login with smb dialect 2.1 and perform a tree connect. It has been extended
|
|
to handle many more cases, but it is far from complete. Many of the structs that can
|
|
be serialized correctly cannot be deserialized and the other way around. Furthermore,
|
|
a few cases has been added that have yet to be used anywhere and as such the code is
|
|
untested.
|
|
*/
|
|
package encoder
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jfjallid/golog"
|
|
)
|
|
|
|
var log = golog.Get("github.com/jfjallid/go-smb/smb/encoder")
|
|
|
|
type BinaryMarshallable interface {
|
|
MarshalBinary(*Metadata) ([]byte, error)
|
|
UnmarshalBinary([]byte, *Metadata) error
|
|
}
|
|
|
|
type Metadata struct {
|
|
Tags *TagMap
|
|
Lens map[string]uint64
|
|
Offsets map[string]uint64
|
|
Counts map[string]uint64
|
|
Parent interface{}
|
|
ParentBuf []byte
|
|
CurrOffset uint64
|
|
CurrField string
|
|
}
|
|
|
|
type TagMap struct {
|
|
m map[string]interface{}
|
|
has map[string]bool
|
|
}
|
|
|
|
func (t TagMap) Has(key string) bool {
|
|
return t.has[key]
|
|
}
|
|
|
|
func (t TagMap) Set(key string, val interface{}) {
|
|
t.m[key] = val
|
|
t.has[key] = true
|
|
}
|
|
|
|
func (t TagMap) Get(key string) interface{} {
|
|
return t.m[key]
|
|
}
|
|
|
|
func (t TagMap) GetInt(key string) (int, error) {
|
|
if !t.Has(key) {
|
|
return 0, errors.New("Key does not exist in tag")
|
|
}
|
|
return t.Get(key).(int), nil
|
|
}
|
|
|
|
func (t TagMap) GetString(key string) (string, error) {
|
|
if !t.Has(key) {
|
|
return "", errors.New("Key does not exist in tag")
|
|
}
|
|
return t.Get(key).(string), nil
|
|
}
|
|
|
|
func parseTags(sf reflect.StructField) (*TagMap, error) {
|
|
ret := &TagMap{
|
|
m: make(map[string]interface{}),
|
|
has: make(map[string]bool),
|
|
}
|
|
tag := sf.Tag.Get("smb")
|
|
smbTags := strings.Split(tag, ",")
|
|
for _, smbTag := range smbTags {
|
|
tokens := strings.Split(smbTag, ":")
|
|
switch tokens[0] {
|
|
case "len", "offset", "count":
|
|
if len(tokens) != 2 {
|
|
return nil, errors.New("Missing required tag data. Expecting key:val")
|
|
}
|
|
ret.Set(tokens[0], tokens[1])
|
|
case "fixed", "align":
|
|
if len(tokens) != 2 {
|
|
return nil, errors.New("Missing required tag data. Expecting key:val")
|
|
}
|
|
i, err := strconv.Atoi(tokens[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret.Set(tokens[0], i)
|
|
case "asn1":
|
|
ret.Set(tokens[0], true)
|
|
case "omitempty":
|
|
if len(tokens) != 2 {
|
|
return nil, errors.New("Missing required tag data. Expecting key:val")
|
|
}
|
|
i, err := strconv.Atoi(tokens[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret.Set(tokens[0], i)
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func getOffsetByFieldName(fieldName string, meta *Metadata) (uint64, error) {
|
|
if meta == nil || meta.Tags == nil || meta.Parent == nil || meta.Lens == nil {
|
|
return 0, errors.New("Cannot determine field offset. Missing required metadata")
|
|
}
|
|
var ret uint64
|
|
var found bool
|
|
parentvf := reflect.Indirect(reflect.ValueOf(meta.Parent))
|
|
// To determine offset, we loop through all fields of the struct, summing lengths of previous elements
|
|
// until we reach our field
|
|
for i := 0; i < parentvf.NumField(); i++ {
|
|
tf := parentvf.Type().Field(i)
|
|
if tf.Name == fieldName {
|
|
found = true
|
|
break
|
|
}
|
|
if l, ok := meta.Lens[tf.Name]; ok {
|
|
// Length of field is in cache
|
|
ret += l
|
|
} else {
|
|
// Not in cache. Must marshal field to determine length. Add to cache after
|
|
buf, err := Marshal(parentvf.Field(i).Interface())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
l := uint64(len(buf))
|
|
meta.Lens[tf.Name] = l
|
|
ret += l
|
|
}
|
|
}
|
|
if !found {
|
|
return 0, errors.New("Cannot find field name within struct: " + fieldName)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func getFieldLengthByName(fieldName string, meta *Metadata) (uint64, error) {
|
|
var ret uint64
|
|
if meta == nil || meta.Tags == nil || meta.Parent == nil || meta.Lens == nil {
|
|
return 0, errors.New("Cannot determine field length. Missing required metadata")
|
|
}
|
|
|
|
// Check if length is stored in field length cache
|
|
if val, ok := meta.Lens[fieldName]; ok {
|
|
return uint64(val), nil
|
|
}
|
|
|
|
parentvf := reflect.Indirect(reflect.ValueOf(meta.Parent))
|
|
|
|
field := parentvf.FieldByName(fieldName)
|
|
if !field.IsValid() {
|
|
fmt.Printf("Cannot determine length of field %s\n", fieldName)
|
|
fmt.Println(meta.Lens)
|
|
return 0, errors.New("Invalid field. Cannot determine length.")
|
|
}
|
|
|
|
bm, ok := field.Interface().(BinaryMarshallable)
|
|
if ok {
|
|
// Custom marshallable interface found.
|
|
buf, err := bm.(BinaryMarshallable).MarshalBinary(meta)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint64(len(buf)), nil
|
|
}
|
|
|
|
if field.Kind() == reflect.Ptr {
|
|
field = field.Elem()
|
|
}
|
|
|
|
switch field.Kind() {
|
|
case reflect.Struct:
|
|
buf, err := Marshal(field.Interface())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
ret = uint64(len(buf))
|
|
case reflect.Interface:
|
|
return 0, errors.New("Interface length calculation not implemented")
|
|
case reflect.Slice, reflect.Array:
|
|
switch field.Type().Elem().Kind() {
|
|
case reflect.Uint8:
|
|
ret = uint64(len(field.Interface().([]byte)))
|
|
case reflect.Uint16:
|
|
ret = uint64(len(field.Interface().([]uint16))) //TODO Is this correct?
|
|
default:
|
|
return 0, errors.New("Cannot calculate the length of unknown slice type for " + fieldName)
|
|
}
|
|
case reflect.Uint8:
|
|
ret = uint64(binary.Size(field.Interface().(uint8)))
|
|
case reflect.Uint16:
|
|
ret = uint64(binary.Size(field.Interface().(uint16)))
|
|
case reflect.Uint32:
|
|
ret = uint64(binary.Size(field.Interface().(uint32)))
|
|
case reflect.Uint64:
|
|
ret = uint64(binary.Size(field.Interface().(uint64)))
|
|
default:
|
|
return 0, errors.New("Cannot calculate the length of unknown kind for field " + fieldName)
|
|
}
|
|
meta.Lens[fieldName] = ret
|
|
return ret, nil
|
|
}
|
|
|
|
func Marshal(v interface{}) ([]byte, error) {
|
|
return marshal(v, nil)
|
|
}
|
|
|
|
func marshal(v interface{}, meta *Metadata) ([]byte, error) {
|
|
var ret []byte
|
|
typev := reflect.TypeOf(v)
|
|
valuev := reflect.ValueOf(v)
|
|
|
|
bm, ok := v.(BinaryMarshallable)
|
|
if ok {
|
|
// Custom marshallable interface found.
|
|
buf, err := bm.MarshalBinary(meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
if typev.Kind() == reflect.Ptr {
|
|
valuev = reflect.Indirect(reflect.ValueOf(v))
|
|
if !valuev.IsValid() {
|
|
// Workaround for struct pointers that should not be included
|
|
if meta != nil && meta.Tags.Has("omitempty") {
|
|
return nil, nil
|
|
} else {
|
|
// Workaround to handle null pointers represented as uint32
|
|
return []byte{0, 0, 0, 0}, nil
|
|
}
|
|
}
|
|
typev = valuev.Type()
|
|
}
|
|
|
|
w := bytes.NewBuffer(ret)
|
|
switch typev.Kind() {
|
|
case reflect.Struct:
|
|
m := &Metadata{
|
|
Tags: &TagMap{},
|
|
Lens: make(map[string]uint64),
|
|
Parent: v,
|
|
}
|
|
for j := 0; j < valuev.NumField(); j++ {
|
|
tags, err := parseTags(typev.Field(j))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.Tags = tags
|
|
buf, err := marshal(valuev.Field(j).Interface(), m)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.Lens[typev.Field(j).Name] = uint64(len(buf))
|
|
if err := binary.Write(w, binary.LittleEndian, buf); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
case reflect.Slice, reflect.Array:
|
|
switch typev.Elem().Kind() {
|
|
case reflect.Uint8:
|
|
if err := binary.Write(w, binary.LittleEndian, v.([]uint8)); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint16:
|
|
if err := binary.Write(w, binary.LittleEndian, v.([]uint16)); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint32:
|
|
if err := binary.Write(w, binary.LittleEndian, v.([]uint32)); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint64:
|
|
if err := binary.Write(w, binary.LittleEndian, v.([]uint64)); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Struct:
|
|
if valuev.Len() == 0 {
|
|
// Empty array
|
|
if err := binary.Write(w, binary.LittleEndian, []byte{0, 0, 0, 0}); err != nil {
|
|
return nil, err
|
|
}
|
|
//TODO Add support for non empty arrays
|
|
//} else {
|
|
// for i:=0; i < valuev.Len(); i++ {
|
|
|
|
// }
|
|
} else {
|
|
for j := 0; j < valuev.Len(); j++ {
|
|
buf, err := marshal(valuev.Index(j).Interface(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, buf); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
//TODO Perhaps I should record the length of the array in a tag or field?
|
|
}
|
|
default:
|
|
err := fmt.Errorf("Want to marshal slice of unknown type: %v\n", typev.Elem().Kind())
|
|
log.Errorln(err)
|
|
return nil, err // Originally this error was ignored
|
|
}
|
|
case reflect.Uint8:
|
|
if err := binary.Write(w, binary.LittleEndian, valuev.Interface().(uint8)); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint16:
|
|
data := valuev.Interface().(uint16)
|
|
if meta != nil && meta.Tags.Has("len") {
|
|
fieldName, err := meta.Tags.GetString("len")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := getFieldLengthByName(fieldName, meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data = uint16(l)
|
|
}
|
|
if meta != nil && meta.Tags.Has("offset") {
|
|
fieldName, err := meta.Tags.GetString("offset")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := getOffsetByFieldName(fieldName, meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data = uint16(l)
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, data); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint32:
|
|
data := valuev.Interface().(uint32)
|
|
if meta != nil && meta.Tags.Has("len") {
|
|
fieldName, err := meta.Tags.GetString("len")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := getFieldLengthByName(fieldName, meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data = uint32(l)
|
|
}
|
|
if meta != nil && meta.Tags.Has("offset") {
|
|
fieldName, err := meta.Tags.GetString("offset")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l, err := getOffsetByFieldName(fieldName, meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//If the buffer length is 0, no need to encode the offset
|
|
// Perhaps this should be handled in getOffsetByFieldName function?
|
|
emptyBuffer := false
|
|
if val, ok := meta.Lens[fieldName]; ok {
|
|
if val == 0 {
|
|
emptyBuffer = true
|
|
}
|
|
}
|
|
if emptyBuffer {
|
|
data = uint32(0)
|
|
} else {
|
|
data = uint32(l)
|
|
}
|
|
}
|
|
if meta != nil && meta.Tags.Has("omitempty") {
|
|
omitVal, _ := meta.Tags.GetInt("omitempty")
|
|
if data == uint32(omitVal) {
|
|
return nil, nil
|
|
}
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, data); err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Uint64:
|
|
if err := binary.Write(w, binary.LittleEndian, valuev.Interface().(uint64)); err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
err := fmt.Errorf("Marshal not implemented for kind: %s", typev.Kind())
|
|
log.Errorln(err)
|
|
return nil, err
|
|
}
|
|
return w.Bytes(), nil
|
|
}
|
|
|
|
func unmarshal(buf []byte, v interface{}, meta *Metadata) (interface{}, error) {
|
|
typev := reflect.TypeOf(v)
|
|
valuev := reflect.ValueOf(v)
|
|
|
|
bm, ok := v.(BinaryMarshallable)
|
|
if ok {
|
|
// Custom marshallable interface found.
|
|
if err := bm.UnmarshalBinary(buf, meta); err != nil {
|
|
return nil, err
|
|
}
|
|
if meta != nil {
|
|
if val, ok := meta.Lens[meta.CurrField]; ok {
|
|
meta.CurrOffset += val
|
|
}
|
|
}
|
|
return bm, nil
|
|
}
|
|
|
|
if typev.Kind() == reflect.Ptr {
|
|
valuev = reflect.ValueOf(v).Elem()
|
|
typev = valuev.Type()
|
|
}
|
|
|
|
if meta == nil {
|
|
meta = &Metadata{
|
|
Tags: &TagMap{},
|
|
Lens: make(map[string]uint64),
|
|
Parent: v,
|
|
ParentBuf: buf,
|
|
Offsets: make(map[string]uint64),
|
|
Counts: make(map[string]uint64),
|
|
CurrOffset: 0,
|
|
}
|
|
}
|
|
|
|
r := bytes.NewBuffer(buf)
|
|
switch typev.Kind() {
|
|
case reflect.Struct:
|
|
m := &Metadata{
|
|
Tags: &TagMap{},
|
|
Lens: make(map[string]uint64),
|
|
Parent: v,
|
|
ParentBuf: buf,
|
|
Offsets: make(map[string]uint64),
|
|
Counts: make(map[string]uint64),
|
|
CurrOffset: 0,
|
|
}
|
|
for i := 0; i < typev.NumField(); i++ {
|
|
m.CurrField = typev.Field(i).Name
|
|
tags, err := parseTags(typev.Field(i))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.Tags = tags
|
|
var data interface{}
|
|
switch typev.Field(i).Type.Kind() {
|
|
case reflect.Struct:
|
|
data, err = unmarshal(buf[m.CurrOffset:], valuev.Field(i).Addr().Interface(), m)
|
|
default:
|
|
data, err = unmarshal(buf[m.CurrOffset:], valuev.Field(i).Interface(), m)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//if (valuev.Field(i).Kind() == reflect.Ptr) && (reflect.TypeOf(data).Kind() == reflect.Struct) {
|
|
// fmt.Println(valuev.Field(i).Type())
|
|
// fmt.Printf("Target Kind: %v, Data kind: %v\n", valuev.Field(i).Kind(), reflect.TypeOf(data).Kind())
|
|
// //valuev.Field(i).Set(reflect.TypeOf(data))
|
|
// t := reflect.ValueOf(data)
|
|
// valuev.Field(i).Set(t)
|
|
//} else {
|
|
// valuev.Field(i).Set(reflect.ValueOf(data))
|
|
//}
|
|
//fmt.Println(reflect.ValueOf(data))
|
|
|
|
// Handle nil results
|
|
if data == nil {
|
|
continue
|
|
}
|
|
valuev.Field(i).Set(reflect.ValueOf(data))
|
|
}
|
|
v = reflect.Indirect(reflect.ValueOf(v)).Interface()
|
|
meta.CurrOffset += m.CurrOffset
|
|
return v, nil
|
|
case reflect.Uint8:
|
|
var ret uint8
|
|
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
|
return nil, err
|
|
}
|
|
meta.CurrOffset += uint64(binary.Size(ret))
|
|
if meta.Tags.Has("count") {
|
|
ref, err := meta.Tags.GetString("count")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Counts[ref] = uint64(ret)
|
|
}
|
|
|
|
return ret, nil
|
|
case reflect.Uint16:
|
|
var ret uint16
|
|
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
|
return nil, err
|
|
}
|
|
if meta.Tags.Has("len") {
|
|
ref, err := meta.Tags.GetString("len")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Lens[ref] = uint64(ret)
|
|
} else if meta.Tags.Has("offset") {
|
|
ref, err := meta.Tags.GetString("offset")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Offsets[ref] = uint64(ret)
|
|
}
|
|
if meta.Tags.Has("count") {
|
|
ref, err := meta.Tags.GetString("count")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Counts[ref] = uint64(ret)
|
|
}
|
|
meta.CurrOffset += uint64(binary.Size(ret))
|
|
return ret, nil
|
|
case reflect.Uint32:
|
|
var ret uint32
|
|
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
|
return nil, err
|
|
}
|
|
if meta.Tags.Has("len") {
|
|
ref, err := meta.Tags.GetString("len")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Lens[ref] = uint64(ret)
|
|
} else if meta.Tags.Has("offset") {
|
|
ref, err := meta.Tags.GetString("offset")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Offsets[ref] = uint64(ret)
|
|
}
|
|
if meta.Tags.Has("count") {
|
|
ref, err := meta.Tags.GetString("count")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Counts[ref] = uint64(ret)
|
|
}
|
|
meta.CurrOffset += uint64(binary.Size(ret))
|
|
return ret, nil
|
|
case reflect.Uint64:
|
|
var ret uint64
|
|
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
|
return nil, err
|
|
}
|
|
if meta.Tags.Has("count") {
|
|
ref, err := meta.Tags.GetString("count")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
meta.Counts[ref] = uint64(ret)
|
|
}
|
|
meta.CurrOffset += uint64(binary.Size(ret))
|
|
return ret, nil
|
|
case reflect.Slice, reflect.Array:
|
|
switch typev.Elem().Kind() {
|
|
case reflect.Uint8:
|
|
var length, offset int
|
|
var err error
|
|
if meta.Tags.Has("fixed") {
|
|
if length, err = meta.Tags.GetInt("fixed"); err != nil {
|
|
return nil, err
|
|
}
|
|
// Fixed length fields advance current offset
|
|
meta.CurrOffset += uint64(length)
|
|
} else if meta.Tags.Has("align") {
|
|
// Align the next struct field to specified align-byte boundary
|
|
// e.g, to 4 byte boundary
|
|
align, err := meta.Tags.GetInt("align")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diff := int(meta.CurrOffset % uint64(align))
|
|
diff = align - diff
|
|
diff = diff % align
|
|
//TODO Below was original align, but does not work as intended when no alignment is needed.
|
|
// However, might break some request where padding is desired rather than alignment such that padd: 0 -> padd=align
|
|
//if diff == 0 {
|
|
// diff = align
|
|
//} else {
|
|
// diff = align - diff
|
|
//}
|
|
if len(meta.ParentBuf) > int(meta.CurrOffset) {
|
|
// Don't increment CurrOffset if we've reached the end of the buffer
|
|
// Meant to handle situations where the alignment padding is located
|
|
// before an optional last element of the struct
|
|
meta.CurrOffset += uint64(diff)
|
|
}
|
|
// NOTE This assumes that length is 0 so nothing is read into data below
|
|
} else {
|
|
if val, ok := meta.Lens[meta.CurrField]; ok {
|
|
length = int(val)
|
|
} else {
|
|
err := fmt.Errorf("Variable length field missing length reference in struct field: " + meta.CurrField)
|
|
log.Errorln(err)
|
|
return nil, err
|
|
}
|
|
if val, ok := meta.Offsets[meta.CurrField]; ok {
|
|
offset = int(val)
|
|
} else {
|
|
// No offset found in map. Use current offset
|
|
offset = int(meta.CurrOffset)
|
|
}
|
|
if offset != int(meta.CurrOffset) {
|
|
// Variable length data is relative to parent/outer struct. Reset reader to point to beginning of data
|
|
r = bytes.NewBuffer(meta.ParentBuf[offset : offset+length])
|
|
// Variable length data fields do NOT advance current offset.
|
|
} else {
|
|
meta.CurrOffset += uint64(length)
|
|
}
|
|
}
|
|
data := make([]byte, length)
|
|
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
case reflect.Uint16:
|
|
var length, offset int
|
|
var err error
|
|
if meta.Tags.Has("fixed") {
|
|
if length, err = meta.Tags.GetInt("fixed"); err != nil {
|
|
return nil, err
|
|
}
|
|
// Fixed length fields advance current offset
|
|
meta.CurrOffset += uint64(length * 2) //TODO Should this be x2? Was originally only length
|
|
} else {
|
|
if val, ok := meta.Counts[meta.CurrField]; ok {
|
|
length = int(val)
|
|
} else {
|
|
if val, ok := meta.Lens[meta.CurrField]; ok {
|
|
length = int(val)
|
|
} else {
|
|
err := fmt.Errorf("Variable length field missing length reference in struct field: " + meta.CurrField)
|
|
log.Errorln(err)
|
|
return nil, err
|
|
}
|
|
if val, ok := meta.Offsets[meta.CurrField]; ok {
|
|
offset = int(val)
|
|
} else {
|
|
// No offset found in map. Use current offset
|
|
offset = int(meta.CurrOffset)
|
|
}
|
|
if offset != int(meta.CurrOffset) {
|
|
// Variable length data is relative to parent/outer struct. Reset reader to point to beginning of data
|
|
r = bytes.NewBuffer(meta.ParentBuf[offset : offset+length*2]) //TODO Should this be x2? Was originally only length
|
|
// Variable length data fields do NOT advance current offset.
|
|
} else {
|
|
meta.CurrOffset += uint64(length * 2) //TODO Should this be x2? Was originally only length
|
|
}
|
|
}
|
|
}
|
|
data := make([]uint16, length)
|
|
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
case reflect.Uint32:
|
|
var length, count int
|
|
var err error
|
|
var data []uint32
|
|
if meta.Tags.Has("fixed") {
|
|
if length, err = meta.Tags.GetInt("fixed"); err != nil {
|
|
return nil, err
|
|
}
|
|
// Fixed length fields advance current offset
|
|
meta.CurrOffset += uint64(length)
|
|
data = make([]uint32, length/4)
|
|
} else {
|
|
if val, ok := meta.Counts[meta.CurrField]; ok {
|
|
count = int(val)
|
|
} else {
|
|
return nil, errors.New("Variable length (uint32) field missing count reference in struct field: " + meta.CurrField)
|
|
}
|
|
meta.CurrOffset += uint64(count * 4)
|
|
data = make([]uint32, count)
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
case reflect.Struct:
|
|
var count uint64
|
|
var ok bool
|
|
if count, ok = meta.Counts[meta.CurrField]; ok {
|
|
if count == 0 {
|
|
return nil, nil
|
|
}
|
|
} else {
|
|
err := fmt.Errorf("NOT IMPLEMENTED unmarshal of slice of structs missing Count tag")
|
|
fmt.Println(err)
|
|
return nil, err
|
|
}
|
|
list := reflect.MakeSlice(typev, 0, int(count))
|
|
arrayOffset := uint64(0)
|
|
prevCurrMetaOffset := uint64(0)
|
|
for i := uint64(0); i < count; i++ {
|
|
prevCurrMetaOffset = meta.CurrOffset
|
|
x := reflect.New(typev.Elem())
|
|
data, err := unmarshal(buf[arrayOffset:], x.Interface(), meta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
arrayOffset += meta.CurrOffset - prevCurrMetaOffset
|
|
list = reflect.Append(list, reflect.ValueOf(data))
|
|
}
|
|
|
|
return list.Interface(), nil
|
|
|
|
default:
|
|
err := fmt.Errorf("Unmarshal not implemented for slice kind:" + typev.Kind().String())
|
|
log.Errorln(err)
|
|
return nil, err
|
|
}
|
|
default:
|
|
err := fmt.Errorf("Unmarshal not implemented for kind:" + typev.Kind().String())
|
|
log.Errorln(err)
|
|
return nil, err
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func Unmarshal(buf []byte, v interface{}) error {
|
|
_, err := unmarshal(buf, v, nil)
|
|
return err
|
|
}
|