mirror of
https://github.com/hanzokms/kmip.git
synced 2026-10-30 20:33:03 +00:00
472 lines
9.0 KiB
Go
472 lines
9.0 KiB
Go
package kmip
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"io"
|
|
"io/ioutil"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Decoder implements KMIP protocol decoding
|
|
//
|
|
// Decoding works exactly the same way as encoding
|
|
// (see Encoder documentation), but the other way around.
|
|
type Decoder struct {
|
|
r io.Reader
|
|
s io.ByteScanner
|
|
|
|
lastTag Tag
|
|
}
|
|
|
|
// NewDecoder builds Decoder which reads from r
|
|
//
|
|
// Buffering can be disabled by providing reader
|
|
// which implements io.ByteScanner
|
|
func NewDecoder(r io.Reader) *Decoder {
|
|
d := &Decoder{
|
|
r: r,
|
|
}
|
|
|
|
if s, ok := r.(io.ByteScanner); ok {
|
|
d.s = s
|
|
} else {
|
|
br := bufio.NewReader(r)
|
|
d.r = br
|
|
d.s = br
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
func (d *Decoder) internalReadTag() (t Tag, err error) {
|
|
var b [3]byte
|
|
|
|
_, err = io.ReadFull(d.r, b[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
t = Tag(binary.BigEndian.Uint32(append([]byte{0}, b[:]...)))
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) readTag() (t Tag, err error) {
|
|
if d.lastTag != 0 {
|
|
t, d.lastTag = d.lastTag, 0
|
|
return
|
|
}
|
|
|
|
t, err = d.internalReadTag()
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) peekTag() (t Tag, err error) {
|
|
if d.lastTag != 0 {
|
|
return d.lastTag, nil
|
|
}
|
|
|
|
d.lastTag, err = d.internalReadTag()
|
|
t = d.lastTag
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) expectTag(expected Tag) error {
|
|
t, err := d.readTag()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if expected != t && expected != ANY_TAG {
|
|
return errors.Errorf("expecting tag %x, but %x was encountered", expected, t)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) readType() (t Type, err error) {
|
|
var b byte
|
|
|
|
b, err = d.s.ReadByte()
|
|
t = Type(b)
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) expectType(expected Type) error {
|
|
t, err := d.readType()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if expected != t {
|
|
return errors.Errorf("expecting type %d, but %d was encountered", expected, t)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) readLength() (l uint32, err error) {
|
|
var b [4]byte
|
|
|
|
_, err = io.ReadFull(d.r, b[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
l = binary.BigEndian.Uint32(b[:])
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) expectLength(expected uint32) error {
|
|
l, err := d.readLength()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if expected != l {
|
|
return errors.Errorf("expecting length %d, but %d was encountered", expected, l)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Decode structure from the reader into v
|
|
func (d *Decoder) Decode(v interface{}) error {
|
|
rv := reflect.ValueOf(v)
|
|
if !rv.IsValid() {
|
|
return errors.New("invalid value")
|
|
}
|
|
if rv.Kind() == reflect.Ptr {
|
|
rv = rv.Elem()
|
|
}
|
|
if !rv.IsValid() {
|
|
return errors.New("invalid pointer value")
|
|
}
|
|
if !rv.CanSet() {
|
|
return errors.New("unsettable value")
|
|
}
|
|
|
|
structDesc, err := getStructDesc(rv.Type())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = d.decode(rv, structDesc)
|
|
return err
|
|
}
|
|
|
|
func (d *Decoder) decodeValue(f field, t reflect.Type, ff reflect.Value) (n int, v interface{}, err error) {
|
|
if f.skip {
|
|
if err = d.expectTag(f.tag); err != nil {
|
|
return
|
|
}
|
|
|
|
if _, err = d.readType(); err != nil {
|
|
return
|
|
}
|
|
|
|
var l uint32
|
|
if l, err = d.readLength(); err != nil {
|
|
return
|
|
}
|
|
|
|
n = 8
|
|
|
|
if l%8 != 0 {
|
|
l += 8 - l%8
|
|
}
|
|
|
|
_, err = io.CopyN(ioutil.Discard, d.r, int64(l))
|
|
n += int(l)
|
|
|
|
return
|
|
}
|
|
|
|
switch f.typ {
|
|
case INTEGER:
|
|
v, err = d.readInteger(f.tag)
|
|
n = 16
|
|
case LONG_INTEGER:
|
|
v, err = d.readLongInteger(f.tag)
|
|
n = 16
|
|
case ENUMERATION:
|
|
v, err = d.readEnum(f.tag)
|
|
n = 16
|
|
case BOOLEAN:
|
|
v, err = d.readBool(f.tag)
|
|
n = 16
|
|
case DATE_TIME:
|
|
v, err = d.readTime(f.tag)
|
|
n = 16
|
|
case INTERVAL:
|
|
v, err = d.readDuration(f.tag)
|
|
n = 16
|
|
case BYTE_STRING:
|
|
n, v, err = d.readBytes(f.tag)
|
|
case TEXT_STRING:
|
|
n, v, err = d.readString(f.tag)
|
|
case STRUCTURE:
|
|
var (
|
|
sD *structDesc
|
|
vv reflect.Value
|
|
)
|
|
|
|
if f.dynamic {
|
|
dD, ok := ff.Addr().Interface().(DynamicDispatch)
|
|
if !ok {
|
|
err = errors.New("field is dynamic, but DynamicDispatch is not implemented")
|
|
return
|
|
}
|
|
|
|
var val interface{}
|
|
|
|
val, err = dD.BuildFieldValue(f.name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
vv = reflect.ValueOf(val)
|
|
|
|
switch vv.Type() {
|
|
case typeOfInt32:
|
|
f.typ = INTEGER
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfInt64:
|
|
f.typ = LONG_INTEGER
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfEnum:
|
|
f.typ = ENUMERATION
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfBool:
|
|
f.typ = BOOLEAN
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfBytes:
|
|
f.typ = BYTE_STRING
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfString:
|
|
f.typ = TEXT_STRING
|
|
return d.decodeValue(f, t, ff)
|
|
case typeOfTime:
|
|
f.typ = DATE_TIME
|
|
return d.decodeValue(f, t, ff)
|
|
}
|
|
|
|
sD, err = getStructDesc(vv.Type().Elem())
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
|
|
sD, err = getStructDesc(t)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
vv = reflect.New(t)
|
|
}
|
|
|
|
sD.tag = f.tag
|
|
n, err = d.decode(vv, sD)
|
|
|
|
v = vv.Elem().Interface()
|
|
default:
|
|
panic("unsupported type")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Decoder) decode(rv reflect.Value, structD *structDesc) (n int, err error) {
|
|
if rv.Kind() == reflect.Ptr {
|
|
rv = rv.Elem()
|
|
}
|
|
|
|
if err = d.expectTag(structD.tag); err != nil {
|
|
return
|
|
}
|
|
|
|
if err = d.expectType(STRUCTURE); err != nil {
|
|
return
|
|
}
|
|
|
|
n += 4
|
|
|
|
var expectedLen, actualLen uint32
|
|
if expectedLen, err = d.readLength(); err != nil {
|
|
return
|
|
}
|
|
|
|
n += 4
|
|
|
|
// initialize wrapped decoder with limited reader
|
|
dd := NewDecoder(io.LimitReader(d.r, int64(expectedLen)))
|
|
|
|
for _, f := range structD.fields {
|
|
var tag Tag
|
|
tag, err = dd.peekTag()
|
|
|
|
if err == io.EOF && !f.required {
|
|
err = nil
|
|
continue
|
|
}
|
|
|
|
if err != nil {
|
|
err = errors.Wrapf(err, "error reading field %v", f.name)
|
|
return
|
|
}
|
|
|
|
if !f.required && tag != f.tag && f.tag != ANY_TAG {
|
|
continue
|
|
}
|
|
|
|
var (
|
|
nn int
|
|
v interface{}
|
|
)
|
|
|
|
ff := rv.FieldByIndex(f.idx)
|
|
|
|
if f.sliceof {
|
|
ff.Set(reflect.MakeSlice(ff.Type(), 0, 0))
|
|
|
|
for {
|
|
nn, v, err = dd.decodeValue(f, ff.Type().Elem(), rv)
|
|
if err != nil {
|
|
// For dynamic slice fields with type mismatches, skip individual items
|
|
if f.dynamic && !f.required && strings.Contains(err.Error(), "expecting type") {
|
|
// Type mismatch occurred. Tag and Type have been read already.
|
|
// We need to read Length and skip Value bytes to continue
|
|
var skipLen uint32
|
|
skipLen, err = dd.readLength()
|
|
if err != nil {
|
|
err = errors.Wrapf(err, "error skipping field %v after type mismatch", f.name)
|
|
return
|
|
}
|
|
|
|
// Skip the value bytes (with padding)
|
|
skipBytes := int64(skipLen)
|
|
if skipLen%8 != 0 {
|
|
skipBytes += int64(8 - skipLen%8)
|
|
}
|
|
_, err = io.CopyN(ioutil.Discard, dd.r, skipBytes)
|
|
if err != nil {
|
|
err = errors.Wrapf(err, "error skipping field %v value bytes", f.name)
|
|
return
|
|
}
|
|
|
|
// Account for bytes read: tag(3) + type(1) + length(4) + value+padding
|
|
nn = 8 + int(skipBytes)
|
|
n += nn
|
|
actualLen += uint32(nn)
|
|
|
|
// Clear error and check if we should continue
|
|
err = nil
|
|
|
|
if actualLen >= expectedLen {
|
|
break
|
|
}
|
|
|
|
tag, err = dd.peekTag()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if tag != f.tag {
|
|
break
|
|
}
|
|
|
|
// Skip this item and continue to next
|
|
continue
|
|
}
|
|
|
|
err = errors.Wrapf(err, "error reading field %v", f.name)
|
|
return
|
|
}
|
|
|
|
n += nn
|
|
actualLen += uint32(nn)
|
|
|
|
if !f.skip {
|
|
ff.Set(reflect.Append(ff, reflect.ValueOf(v)))
|
|
}
|
|
|
|
if actualLen >= expectedLen {
|
|
break
|
|
}
|
|
|
|
tag, err = dd.peekTag()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if tag != f.tag {
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
nn, v, err = dd.decodeValue(f, ff.Type(), rv)
|
|
if err != nil {
|
|
// For dynamic fields with type mismatches (e.g. unsupported structured attributes),
|
|
// skip the field and continue if not required
|
|
if f.dynamic && !f.required && strings.Contains(err.Error(), "expecting type") {
|
|
// Type mismatch occurred. Tag and Type have been read already.
|
|
// We need to read Length and skip Value bytes to continue
|
|
var skipLen uint32
|
|
skipLen, err = dd.readLength()
|
|
if err != nil {
|
|
err = errors.Wrapf(err, "error skipping field %v after type mismatch", f.name)
|
|
return
|
|
}
|
|
|
|
// Skip the value bytes (with padding)
|
|
skipBytes := int64(skipLen)
|
|
if skipLen%8 != 0 {
|
|
skipBytes += int64(8 - skipLen%8)
|
|
}
|
|
_, err = io.CopyN(ioutil.Discard, dd.r, skipBytes)
|
|
if err != nil {
|
|
err = errors.Wrapf(err, "error skipping field %v value bytes", f.name)
|
|
return
|
|
}
|
|
|
|
// Account for bytes read: tag(3) + type(1) + length(4) + value+padding
|
|
nn = 8 + int(skipBytes)
|
|
n += nn
|
|
actualLen += uint32(nn)
|
|
|
|
// Clear error and continue to next field
|
|
err = nil
|
|
continue
|
|
}
|
|
|
|
err = errors.Wrapf(err, "error reading field %v", f.name)
|
|
return
|
|
}
|
|
|
|
n += nn
|
|
actualLen += uint32(nn)
|
|
|
|
if !f.skip {
|
|
ff.Set(reflect.ValueOf(v))
|
|
}
|
|
}
|
|
}
|
|
|
|
if actualLen != expectedLen {
|
|
err = errors.Errorf("error reading structure expected %d != actual %d", expectedLen, actualLen)
|
|
}
|
|
|
|
return
|
|
}
|