1package filexfer 2 3import ( 4 "fmt" 5) 6 7// StatusPacket defines the SSH_FXP_STATUS packet. 8// 9// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 10type StatusPacket struct { 11 StatusCode Status 12 ErrorMessage string 13 LanguageTag string 14} 15 16// Error makes StatusPacket an error type. 17func (p *StatusPacket) Error() string { 18 if p.ErrorMessage == "" { 19 return "sftp: " + p.StatusCode.String() 20 } 21 22 return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode) 23} 24 25// Is returns true if target is a StatusPacket with the same StatusCode, 26// or target is a Status code which is the same as SatusCode. 27func (p *StatusPacket) Is(target error) bool { 28 if target, ok := target.(*StatusPacket); ok { 29 return p.StatusCode == target.StatusCode 30 } 31 32 return p.StatusCode == target 33} 34 35// Type returns the SSH_FXP_xy value associated with this packet type. 36func (p *StatusPacket) Type() PacketType { 37 return PacketTypeStatus 38} 39 40// MarshalPacket returns p as a two-part binary encoding of p. 41func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 42 buf := NewBuffer(b) 43 if buf.Cap() < 9 { 44 // uint32(error/status code) + string(error message) + string(language tag) 45 size := 4 + 4 + len(p.ErrorMessage) + 4 + len(p.LanguageTag) 46 buf = NewMarshalBuffer(size) 47 } 48 49 buf.StartPacket(PacketTypeStatus, reqid) 50 buf.AppendUint32(uint32(p.StatusCode)) 51 buf.AppendString(p.ErrorMessage) 52 buf.AppendString(p.LanguageTag) 53 54 return buf.Packet(payload) 55} 56 57// UnmarshalPacketBody unmarshals the packet body from the given Buffer. 58// It is assumed that the uint32(request-id) has already been consumed. 59func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) { 60 statusCode, err := buf.ConsumeUint32() 61 if err != nil { 62 return err 63 } 64 p.StatusCode = Status(statusCode) 65 66 if p.ErrorMessage, err = buf.ConsumeString(); err != nil { 67 return err 68 } 69 70 if p.LanguageTag, err = buf.ConsumeString(); err != nil { 71 return err 72 } 73 74 return nil 75} 76 77// HandlePacket defines the SSH_FXP_HANDLE packet. 78type HandlePacket struct { 79 Handle string 80} 81 82// Type returns the SSH_FXP_xy value associated with this packet type. 83func (p *HandlePacket) Type() PacketType { 84 return PacketTypeHandle 85} 86 87// MarshalPacket returns p as a two-part binary encoding of p. 88func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 89 buf := NewBuffer(b) 90 if buf.Cap() < 9 { 91 size := 4 + len(p.Handle) // string(handle) 92 buf = NewMarshalBuffer(size) 93 } 94 95 buf.StartPacket(PacketTypeHandle, reqid) 96 buf.AppendString(p.Handle) 97 98 return buf.Packet(payload) 99} 100 101// UnmarshalPacketBody unmarshals the packet body from the given Buffer. 102// It is assumed that the uint32(request-id) has already been consumed. 103func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) { 104 if p.Handle, err = buf.ConsumeString(); err != nil { 105 return err 106 } 107 108 return nil 109} 110 111// DataPacket defines the SSH_FXP_DATA packet. 112type DataPacket struct { 113 Data []byte 114} 115 116// Type returns the SSH_FXP_xy value associated with this packet type. 117func (p *DataPacket) Type() PacketType { 118 return PacketTypeData 119} 120 121// MarshalPacket returns p as a two-part binary encoding of p. 122func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 123 buf := NewBuffer(b) 124 if buf.Cap() < 9 { 125 size := 4 // uint32(len(data)); data content in payload 126 buf = NewMarshalBuffer(size) 127 } 128 129 buf.StartPacket(PacketTypeData, reqid) 130 buf.AppendUint32(uint32(len(p.Data))) 131 132 return buf.Packet(p.Data) 133} 134 135// UnmarshalPacketBody unmarshals the packet body from the given Buffer. 136// It is assumed that the uint32(request-id) has already been consumed. 137// 138// If p.Data is already populated, and of sufficient length to hold the data, 139// then this will copy the data into that byte slice. 140// 141// If p.Data has a length insufficient to hold the data, 142// then this will make a new slice of sufficient length, and copy the data into that. 143// 144// This means this _does not_ alias any of the data buffer that is passed in. 145func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) { 146 data, err := buf.ConsumeByteSlice() 147 if err != nil { 148 return err 149 } 150 151 if len(p.Data) < len(data) { 152 p.Data = make([]byte, len(data)) 153 } 154 155 n := copy(p.Data, data) 156 p.Data = p.Data[:n] 157 return nil 158} 159 160// NamePacket defines the SSH_FXP_NAME packet. 161type NamePacket struct { 162 Entries []*NameEntry 163} 164 165// Type returns the SSH_FXP_xy value associated with this packet type. 166func (p *NamePacket) Type() PacketType { 167 return PacketTypeName 168} 169 170// MarshalPacket returns p as a two-part binary encoding of p. 171func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 172 buf := NewBuffer(b) 173 if buf.Cap() < 9 { 174 size := 4 // uint32(len(entries)) 175 176 for _, e := range p.Entries { 177 size += e.Len() 178 } 179 180 buf = NewMarshalBuffer(size) 181 } 182 183 buf.StartPacket(PacketTypeName, reqid) 184 buf.AppendUint32(uint32(len(p.Entries))) 185 186 for _, e := range p.Entries { 187 e.MarshalInto(buf) 188 } 189 190 return buf.Packet(payload) 191} 192 193// UnmarshalPacketBody unmarshals the packet body from the given Buffer. 194// It is assumed that the uint32(request-id) has already been consumed. 195func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) { 196 count, err := buf.ConsumeUint32() 197 if err != nil { 198 return err 199 } 200 201 p.Entries = make([]*NameEntry, 0, count) 202 203 for i := uint32(0); i < count; i++ { 204 var e NameEntry 205 if err := e.UnmarshalFrom(buf); err != nil { 206 return err 207 } 208 209 p.Entries = append(p.Entries, &e) 210 } 211 212 return nil 213} 214 215// AttrsPacket defines the SSH_FXP_ATTRS packet. 216type AttrsPacket struct { 217 Attrs Attributes 218} 219 220// Type returns the SSH_FXP_xy value associated with this packet type. 221func (p *AttrsPacket) Type() PacketType { 222 return PacketTypeAttrs 223} 224 225// MarshalPacket returns p as a two-part binary encoding of p. 226func (p *AttrsPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 227 buf := NewBuffer(b) 228 if buf.Cap() < 9 { 229 size := p.Attrs.Len() // ATTRS(attrs) 230 buf = NewMarshalBuffer(size) 231 } 232 233 buf.StartPacket(PacketTypeAttrs, reqid) 234 p.Attrs.MarshalInto(buf) 235 236 return buf.Packet(payload) 237} 238 239// UnmarshalPacketBody unmarshals the packet body from the given Buffer. 240// It is assumed that the uint32(request-id) has already been consumed. 241func (p *AttrsPacket) UnmarshalPacketBody(buf *Buffer) (err error) { 242 return p.Attrs.UnmarshalFrom(buf) 243} 244