diff options
Diffstat (limited to 'internal/encoding/ssh/filexfer/response_packets.go')
-rw-r--r-- | internal/encoding/ssh/filexfer/response_packets.go | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/internal/encoding/ssh/filexfer/response_packets.go b/internal/encoding/ssh/filexfer/response_packets.go new file mode 100644 index 0000000..7a9b3ea --- /dev/null +++ b/internal/encoding/ssh/filexfer/response_packets.go @@ -0,0 +1,243 @@ +package filexfer + +import ( + "fmt" +) + +// StatusPacket defines the SSH_FXP_STATUS packet. +// +// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 +type StatusPacket struct { + StatusCode Status + ErrorMessage string + LanguageTag string +} + +// Error makes StatusPacket an error type. +func (p *StatusPacket) Error() string { + if p.ErrorMessage == "" { + return "sftp: " + p.StatusCode.String() + } + + return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode) +} + +// Is returns true if target is a StatusPacket with the same StatusCode, +// or target is a Status code which is the same as SatusCode. +func (p *StatusPacket) Is(target error) bool { + if target, ok := target.(*StatusPacket); ok { + return p.StatusCode == target.StatusCode + } + + return p.StatusCode == target +} + +// Type returns the SSH_FXP_xy value associated with this packet type. +func (p *StatusPacket) Type() PacketType { + return PacketTypeStatus +} + +// MarshalPacket returns p as a two-part binary encoding of p. +func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { + buf := NewBuffer(b) + if buf.Cap() < 9 { + // uint32(error/status code) + string(error message) + string(language tag) + size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag) + buf = NewMarshalBuffer(size) + } + + buf.StartPacket(PacketTypeStatus, reqid) + buf.AppendUint32(uint32(p.StatusCode)) + buf.AppendString(p.ErrorMessage) + buf.AppendString(p.LanguageTag) + + return buf.Packet(payload) +} + +// UnmarshalPacketBody unmarshals the packet body from the given Buffer. +// It is assumed that the uint32(request-id) has already been consumed. +func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) { + statusCode, err := buf.ConsumeUint32() + if err != nil { + return err + } + p.StatusCode = Status(statusCode) + + if p.ErrorMessage, err = buf.ConsumeString(); err != nil { + return err + } + + if p.LanguageTag, err = buf.ConsumeString(); err != nil { + return err + } + + return nil +} + +// HandlePacket defines the SSH_FXP_HANDLE packet. +type HandlePacket struct { + Handle string +} + +// Type returns the SSH_FXP_xy value associated with this packet type. +func (p *HandlePacket) Type() PacketType { + return PacketTypeHandle +} + +// MarshalPacket returns p as a two-part binary encoding of p. +func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { + buf := NewBuffer(b) + if buf.Cap() < 9 { + size := 4 + len(p.Handle) // string(handle) + buf = NewMarshalBuffer(size) + } + + buf.StartPacket(PacketTypeHandle, reqid) + buf.AppendString(p.Handle) + + return buf.Packet(payload) +} + +// UnmarshalPacketBody unmarshals the packet body from the given Buffer. +// It is assumed that the uint32(request-id) has already been consumed. +func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) { + if p.Handle, err = buf.ConsumeString(); err != nil { + return err + } + + return nil +} + +// DataPacket defines the SSH_FXP_DATA packet. +type DataPacket struct { + Data []byte +} + +// Type returns the SSH_FXP_xy value associated with this packet type. +func (p *DataPacket) Type() PacketType { + return PacketTypeData +} + +// MarshalPacket returns p as a two-part binary encoding of p. +func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { + buf := NewBuffer(b) + if buf.Cap() < 9 { + size := 4 // uint32(len(data)); data content in payload + buf = NewMarshalBuffer(size) + } + + buf.StartPacket(PacketTypeData, reqid) + buf.AppendUint32(uint32(len(p.Data))) + + return buf.Packet(p.Data) +} + +// 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 already populated, and of sufficient length to hold the data, +// then this will copy the data into that byte slice. +// +// If p.Data has a length insufficient to hold the data, +// then this will make a new slice of sufficient length, and copy the data into that. +// +// This means this _does not_ alias any of the data buffer that is passed in. +func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) { + data, err := buf.ConsumeByteSlice() + if err != nil { + return err + } + + if len(p.Data) < len(data) { + p.Data = make([]byte, len(data)) + } + + n := copy(p.Data, data) + p.Data = p.Data[:n] + return nil +} + +// NamePacket defines the SSH_FXP_NAME packet. +type NamePacket struct { + Entries []*NameEntry +} + +// Type returns the SSH_FXP_xy value associated with this packet type. +func (p *NamePacket) Type() PacketType { + return PacketTypeName +} + +// MarshalPacket returns p as a two-part binary encoding of p. +func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { + buf := NewBuffer(b) + if buf.Cap() < 9 { + size := 4 // uint32(len(entries)) + + for _, e := range p.Entries { + size += e.Len() + } + + buf = NewMarshalBuffer(size) + } + + buf.StartPacket(PacketTypeName, reqid) + buf.AppendUint32(uint32(len(p.Entries))) + + for _, e := range p.Entries { + e.MarshalInto(buf) + } + + return buf.Packet(payload) +} + +// UnmarshalPacketBody unmarshals the packet body from the given Buffer. +// It is assumed that the uint32(request-id) has already been consumed. +func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) { + count, err := buf.ConsumeUint32() + if err != nil { + return err + } + + p.Entries = make([]*NameEntry, 0, count) + + for i := uint32(0); i < count; i++ { + var e NameEntry + if err := e.UnmarshalFrom(buf); err != nil { + return err + } + + p.Entries = append(p.Entries, &e) + } + + return nil +} + +// AttrsPacket defines the SSH_FXP_ATTRS packet. +type AttrsPacket struct { + Attrs Attributes +} + +// Type returns the SSH_FXP_xy value associated with this packet type. +func (p *AttrsPacket) Type() PacketType { + return PacketTypeAttrs +} + +// MarshalPacket returns p as a two-part binary encoding of p. +func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { + buf := NewBuffer(b) + if buf.Cap() < 9 { + size := p.Attrs.Len() // ATTRS(attrs) + buf = NewMarshalBuffer(size) + } + + buf.StartPacket(PacketTypeAttrs, reqid) + p.Attrs.MarshalInto(buf) + + return buf.Packet(payload) +} + +// UnmarshalPacketBody unmarshals the packet body from the given Buffer. +// It is assumed that the uint32(request-id) has already been consumed. +func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) { + return p.Attrs.UnmarshalFrom(buf) +} |