aboutsummaryrefslogtreecommitdiff
path: root/internal/encoding/ssh/filexfer/response_packets.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/encoding/ssh/filexfer/response_packets.go')
-rw-r--r--internal/encoding/ssh/filexfer/response_packets.go243
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)
+}