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