mirror of https://github.com/pkg/sftp.git
encoding: Extended ExtendedReply
This commit is contained in:
parent
d9b8552134
commit
91d29715b2
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue