encoding: Extended ExtendedReply

This commit is contained in:
Cassondra Foesch 2021-03-19 19:54:11 +00:00
parent d9b8552134
commit 91d29715b2
2 changed files with 416 additions and 0 deletions

View File

@ -0,0 +1,151 @@
package filexfer
import (
"encoding"
)
// ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler.
type ExtendedData = interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
}
// ExtendedPacket defines the SSH_FXP_CLOSE packet.
type ExtendedPacket struct {
RequestID uint32
ExtendedRequest string
Data ExtendedData
}
// MarshalPacket returns p as a two-part binary encoding of p.
//
// The Data is marshaled into binary, and returned as the payload.
func (p *ExtendedPacket) MarshalPacket() (header, payload []byte, err error) {
size := 1 + 4 + // byte(type) + uint32(request-id)
4 + len(p.ExtendedRequest) // string(extended-request)
b := NewMarshalBuffer(size)
b.AppendUint8(uint8(PacketTypeExtended))
b.AppendUint32(p.RequestID)
b.AppendString(p.ExtendedRequest)
if p.Data != nil {
payload, err = p.Data.MarshalBinary()
if err != nil {
return nil, nil, err
}
}
b.PutLength(size + len(payload))
return b.Bytes(), payload, nil
}
// MarshalBinary returns p as the binary encoding of p.
func (p *ExtendedPacket) MarshalBinary() ([]byte, error) {
return ComposePacket(p.MarshalPacket())
}
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed.
//
// If p.Data is nil, and there is request-specific-data,
// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.ExtendedRequest, err = buf.ConsumeString(); err != nil {
return err
}
if buf.Len() > 0 {
if p.Data == nil {
p.Data = new(Buffer)
}
if err := p.Data.UnmarshalBinary(buf.Bytes()); err != nil {
return err
}
}
return nil
}
// UnmarshalBinary unmarshals a full raw packet out of the given data.
// It is assumed that the uint32(length) has already been consumed to receive the data.
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
func (p *ExtendedPacket) UnmarshalBinary(data []byte) (err error) {
buf := NewBuffer(data)
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
return err
}
return p.UnmarshalPacketBody(buf)
}
// ExtendedReplyPacket defines the SSH_FXP_CLOSE packet.
type ExtendedReplyPacket struct {
RequestID uint32
Data ExtendedData
}
// MarshalPacket returns p as a two-part binary encoding of p.
//
// The Data is marshaled into binary, and returned as the payload.
func (p *ExtendedReplyPacket) MarshalPacket() (header, payload []byte, err error) {
size := 1 + 4 // byte(type) + uint32(request-id)
b := NewMarshalBuffer(size)
b.AppendUint8(uint8(PacketTypeExtendedReply))
b.AppendUint32(p.RequestID)
if p.Data != nil {
payload, err = p.Data.MarshalBinary()
if err != nil {
return nil, nil, err
}
}
b.PutLength(size + len(payload))
return b.Bytes(), payload, nil
}
// MarshalBinary returns p as the binary encoding of p.
func (p *ExtendedReplyPacket) MarshalBinary() ([]byte, error) {
return ComposePacket(p.MarshalPacket())
}
// UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed.
//
// If p.Data is nil, and there is request-specific-data,
// then the request-specific-data will be wrapped in a Buffer and assigned to p.Data.
func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if buf.Len() > 0 {
if p.Data == nil {
p.Data = new(Buffer)
}
if err := p.Data.UnmarshalBinary(buf.Bytes()); err != nil {
return err
}
}
return nil
}
// UnmarshalBinary unmarshals a full raw packet out of the given data.
// It is assumed that the uint32(length) has already been consumed to receive the data.
// It is also assumed that the uint8(type) has already been consumed to which packet to unmarshal into.
func (p *ExtendedReplyPacket) UnmarshalBinary(data []byte) (err error) {
buf := NewBuffer(data)
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
return err
}
return p.UnmarshalPacketBody(buf)
}

View File

@ -0,0 +1,265 @@
package filexfer
import (
"bytes"
"testing"
)
type testExtendedData struct {
value uint8
}
func (d *testExtendedData) MarshalBinary() ([]byte, error) {
buf := NewBuffer(make([]byte, 0, 4))
buf.AppendUint8(d.value ^ 0x2a)
return buf.Bytes(), nil
}
func (d *testExtendedData) UnmarshalBinary(data []byte) error {
buf := NewBuffer(data)
v, err := buf.ConsumeUint8()
if err != nil {
return err
}
d.value = v ^ 0x2a
return nil
}
func TestExtendedPacketNoData(t *testing.T) {
const (
id = 42
extendedRequest = "foo@example"
)
p := &ExtendedPacket{
RequestID: id,
ExtendedRequest: extendedRequest,
}
data, err := p.MarshalBinary()
if err != nil {
t.Fatal("unexpected error:", err)
}
want := []byte{
0x00, 0x00, 0x00, 20,
200,
0x00, 0x00, 0x00, 42,
0x00, 0x00, 0x00, 11, 'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e',
}
if !bytes.Equal(data, want) {
t.Fatalf("Marshal() = %X, but wanted %X", data, want)
}
*p = ExtendedPacket{}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
if p.ExtendedRequest != extendedRequest {
t.Errorf("UnmarshalBinary(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extendedRequest)
}
}
func TestExtendedPacketTestData(t *testing.T) {
const (
id = 42
extendedRequest = "foo@example"
textValue = 13
)
const value = 13
p := &ExtendedPacket{
RequestID: id,
ExtendedRequest: extendedRequest,
Data: &testExtendedData{
value: textValue,
},
}
data, err := p.MarshalBinary()
if err != nil {
t.Fatal("unexpected error:", err)
}
want := []byte{
0x00, 0x00, 0x00, 21,
200,
0x00, 0x00, 0x00, 42,
0x00, 0x00, 0x00, 11, 'f', 'o', 'o', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e',
0x27,
}
if !bytes.Equal(data, want) {
t.Fatalf("Marshal() = %X, but wanted %X", data, want)
}
*p = ExtendedPacket{
Data: new(testExtendedData),
}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
if p.ExtendedRequest != extendedRequest {
t.Errorf("UnmarshalBinary(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extendedRequest)
}
if data, ok := p.Data.(*testExtendedData); !ok {
t.Errorf("UnmarshalBinary(): Data was type %T, but expected %T", p.Data, data)
} else if data.value != value {
t.Errorf("UnmarshalBinary(): Data.value was %#x, but expected %#x", data.value, value)
}
*p = ExtendedPacket{}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
if p.ExtendedRequest != extendedRequest {
t.Errorf("UnmarshalBinary(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extendedRequest)
}
wantBuffer := []byte{0x27}
if data, ok := p.Data.(*Buffer); !ok {
t.Errorf("UnmarshalBinary(): Data was type %T, but expected %T", p.Data, data)
} else if !bytes.Equal(data.b, wantBuffer) {
t.Errorf("UnmarshalBinary(): Data was %X, but expected %X", data.b, wantBuffer)
}
}
func TestExtendedReplyNoData(t *testing.T) {
const (
id = 42
)
p := &ExtendedReplyPacket{
RequestID: id,
}
data, err := p.MarshalBinary()
if err != nil {
t.Fatal("unexpected error:", err)
}
want := []byte{
0x00, 0x00, 0x00, 5,
201,
0x00, 0x00, 0x00, 42,
}
if !bytes.Equal(data, want) {
t.Fatalf("Marshal() = %X, but wanted %X", data, want)
}
*p = ExtendedReplyPacket{}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
}
func TestExtendedReplyPacketTestData(t *testing.T) {
const (
id = 42
textValue = 13
)
const value = 13
p := &ExtendedReplyPacket{
RequestID: id,
Data: &testExtendedData{
value: textValue,
},
}
data, err := p.MarshalBinary()
if err != nil {
t.Fatal("unexpected error:", err)
}
want := []byte{
0x00, 0x00, 0x00, 6,
201,
0x00, 0x00, 0x00, 42,
0x27,
}
if !bytes.Equal(data, want) {
t.Fatalf("Marshal() = %X, but wanted %X", data, want)
}
*p = ExtendedReplyPacket{
Data: new(testExtendedData),
}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
if data, ok := p.Data.(*testExtendedData); !ok {
t.Errorf("UnmarshalBinary(): Data was type %T, but expected %T", p.Data, data)
} else if data.value != value {
t.Errorf("UnmarshalBinary(): Data.value was %#x, but expected %#x", data.value, value)
}
*p = ExtendedReplyPacket{}
// UnmarshalBinary assumes the uint32(length) + uint8(type) have already been consumed.
if err := p.UnmarshalBinary(data[5:]); err != nil {
t.Fatal("unexpected error:", err)
}
if p.RequestID != uint32(id) {
t.Errorf("UnmarshalBinary(): RequestID was %d, but expected %d", p.RequestID, id)
}
wantBuffer := []byte{0x27}
if data, ok := p.Data.(*Buffer); !ok {
t.Errorf("UnmarshalBinary(): Data was type %T, but expected %T", p.Data, data)
} else if !bytes.Equal(data.b, wantBuffer) {
t.Errorf("UnmarshalBinary(): Data was %X, but expected %X", data.b, wantBuffer)
}
}