github.com/pkg/sftp@v1.13.6/internal/encoding/ssh/filexfer/buffer.go (about) 1 package sshfx 2 3 import ( 4 "encoding/binary" 5 "errors" 6 ) 7 8 // Various encoding errors. 9 var ( 10 ErrShortPacket = errors.New("packet too short") 11 ErrLongPacket = errors.New("packet too long") 12 ) 13 14 // Buffer wraps up the various encoding details of the SSH format. 15 // 16 // Data types are encoded as per section 4 from https://tools.ietf.org/html/draft-ietf-secsh-architecture-09#page-8 17 type Buffer struct { 18 b []byte 19 off int 20 Err error 21 } 22 23 // NewBuffer creates and initializes a new buffer using buf as its initial contents. 24 // The new buffer takes ownership of buf, and the caller should not use buf after this call. 25 // 26 // In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer. 27 func NewBuffer(buf []byte) *Buffer { 28 return &Buffer{ 29 b: buf, 30 } 31 } 32 33 // NewMarshalBuffer creates a new Buffer ready to start marshaling a Packet into. 34 // It preallocates enough space for uint32(length), uint8(type), uint32(request-id) and size more bytes. 35 func NewMarshalBuffer(size int) *Buffer { 36 return NewBuffer(make([]byte, 4+1+4+size)) 37 } 38 39 // Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer. 40 // The slice is valid for use only until the next buffer modification 41 // (that is, only until the next call to an Append or Consume method). 42 func (b *Buffer) Bytes() []byte { 43 return b.b[b.off:] 44 } 45 46 // Len returns the number of unconsumed bytes in the buffer. 47 func (b *Buffer) Len() int { return len(b.b) - b.off } 48 49 // Cap returns the capacity of the buffer’s underlying byte slice, 50 // that is, the total space allocated for the buffer’s data. 51 func (b *Buffer) Cap() int { return cap(b.b) } 52 53 // Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends. 54 func (b *Buffer) Reset() { 55 *b = Buffer{ 56 b: b.b[:0], 57 } 58 } 59 60 // StartPacket resets and initializes the buffer to be ready to start marshaling a packet into. 61 // It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID. 62 func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) { 63 *b = Buffer{ 64 b: append(b.b[:0], make([]byte, 4)...), 65 } 66 67 b.AppendUint8(uint8(packetType)) 68 b.AppendUint32(requestID) 69 } 70 71 // Packet finalizes the packet started from StartPacket. 72 // It is expected that this will end the ownership of the underlying byte-slice, 73 // and so the returned byte-slices may be reused the same as any other byte-slice, 74 // the caller should not use this buffer after this call. 75 // 76 // It writes the packet body length into the first four bytes of the buffer in network byte order (big endian). 77 // The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload. 78 // 79 // It is assumed that no Consume methods have been called on this buffer, 80 // and so it returns the whole underlying slice. 81 func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) { 82 b.PutLength(len(b.b) - 4 + len(payload)) 83 84 return b.b, payload, nil 85 } 86 87 // ConsumeUint8 consumes a single byte from the buffer. 88 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 89 func (b *Buffer) ConsumeUint8() uint8 { 90 if b.Err != nil { 91 return 0 92 } 93 94 if b.Len() < 1 { 95 b.off = len(b.b) 96 b.Err = ErrShortPacket 97 return 0 98 } 99 100 var v uint8 101 v, b.off = b.b[b.off], b.off+1 102 return v 103 } 104 105 // AppendUint8 appends a single byte into the buffer. 106 func (b *Buffer) AppendUint8(v uint8) { 107 b.b = append(b.b, v) 108 } 109 110 // ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero. 111 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 112 func (b *Buffer) ConsumeBool() bool { 113 return b.ConsumeUint8() != 0 114 } 115 116 // AppendBool appends a single bool into the buffer. 117 // It encodes it as a single byte, with false as 0, and true as 1. 118 func (b *Buffer) AppendBool(v bool) { 119 if v { 120 b.AppendUint8(1) 121 } else { 122 b.AppendUint8(0) 123 } 124 } 125 126 // ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian). 127 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 128 func (b *Buffer) ConsumeUint16() uint16 { 129 if b.Err != nil { 130 return 0 131 } 132 133 if b.Len() < 2 { 134 b.off = len(b.b) 135 b.Err = ErrShortPacket 136 return 0 137 } 138 139 v := binary.BigEndian.Uint16(b.b[b.off:]) 140 b.off += 2 141 return v 142 } 143 144 // AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian). 145 func (b *Buffer) AppendUint16(v uint16) { 146 b.b = append(b.b, 147 byte(v>>8), 148 byte(v>>0), 149 ) 150 } 151 152 // unmarshalUint32 is used internally to read the packet length. 153 // It is unsafe, and so not exported. 154 // Even within this package, its use should be avoided. 155 func unmarshalUint32(b []byte) uint32 { 156 return binary.BigEndian.Uint32(b[:4]) 157 } 158 159 // ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian). 160 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 161 func (b *Buffer) ConsumeUint32() uint32 { 162 if b.Err != nil { 163 return 0 164 } 165 166 if b.Len() < 4 { 167 b.off = len(b.b) 168 b.Err = ErrShortPacket 169 return 0 170 } 171 172 v := binary.BigEndian.Uint32(b.b[b.off:]) 173 b.off += 4 174 return v 175 } 176 177 // AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian). 178 func (b *Buffer) AppendUint32(v uint32) { 179 b.b = append(b.b, 180 byte(v>>24), 181 byte(v>>16), 182 byte(v>>8), 183 byte(v>>0), 184 ) 185 } 186 187 // ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int. 188 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 189 func (b *Buffer) ConsumeCount() int { 190 return int(b.ConsumeUint32()) 191 } 192 193 // AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian). 194 func (b *Buffer) AppendCount(v int) { 195 b.AppendUint32(uint32(v)) 196 } 197 198 // ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian). 199 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 200 func (b *Buffer) ConsumeUint64() uint64 { 201 if b.Err != nil { 202 return 0 203 } 204 205 if b.Len() < 8 { 206 b.off = len(b.b) 207 b.Err = ErrShortPacket 208 return 0 209 } 210 211 v := binary.BigEndian.Uint64(b.b[b.off:]) 212 b.off += 8 213 return v 214 } 215 216 // AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian). 217 func (b *Buffer) AppendUint64(v uint64) { 218 b.b = append(b.b, 219 byte(v>>56), 220 byte(v>>48), 221 byte(v>>40), 222 byte(v>>32), 223 byte(v>>24), 224 byte(v>>16), 225 byte(v>>8), 226 byte(v>>0), 227 ) 228 } 229 230 // ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement. 231 // If the buffer does not have enough data, it will set Err to ErrShortPacket. 232 func (b *Buffer) ConsumeInt64() int64 { 233 return int64(b.ConsumeUint64()) 234 } 235 236 // AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement. 237 func (b *Buffer) AppendInt64(v int64) { 238 b.AppendUint64(uint64(v)) 239 } 240 241 // ConsumeByteSlice consumes a single string of raw binary data from the buffer. 242 // A string is a uint32 length, followed by that number of raw bytes. 243 // If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket. 244 // 245 // The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused 246 // (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary). 247 // 248 // In no case will any Consume calls return overlapping slice aliases, 249 // and Append calls are guaranteed to not disturb this slice alias. 250 func (b *Buffer) ConsumeByteSlice() []byte { 251 length := int(b.ConsumeUint32()) 252 if b.Err != nil { 253 return nil 254 } 255 256 if b.Len() < length || length < 0 { 257 b.off = len(b.b) 258 b.Err = ErrShortPacket 259 return nil 260 } 261 262 v := b.b[b.off:] 263 if len(v) > length || cap(v) > length { 264 v = v[:length:length] 265 } 266 b.off += int(length) 267 return v 268 } 269 270 // ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer. 271 // A string is a uint32 length, followed by that number of raw bytes. 272 // If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket. 273 // 274 // The returned slice does not alias any buffer contents, 275 // and will therefore be valid even if the buffer is later reused. 276 // 277 // If hint has sufficient capacity to hold the data, it will be reused and overwritten, 278 // otherwise a new backing slice will be allocated and returned. 279 func (b *Buffer) ConsumeByteSliceCopy(hint []byte) []byte { 280 data := b.ConsumeByteSlice() 281 282 if grow := len(data) - len(hint); grow > 0 { 283 hint = append(hint, make([]byte, grow)...) 284 } 285 286 n := copy(hint, data) 287 hint = hint[:n] 288 return hint 289 } 290 291 // AppendByteSlice appends a single string of raw binary data into the buffer. 292 // A string is a uint32 length, followed by that number of raw bytes. 293 func (b *Buffer) AppendByteSlice(v []byte) { 294 b.AppendUint32(uint32(len(v))) 295 b.b = append(b.b, v...) 296 } 297 298 // ConsumeString consumes a single string of binary data from the buffer. 299 // A string is a uint32 length, followed by that number of raw bytes. 300 // If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket. 301 // 302 // NOTE: Go implicitly assumes that strings contain UTF-8 encoded data. 303 // All caveats on using arbitrary binary data in Go strings applies. 304 func (b *Buffer) ConsumeString() string { 305 return string(b.ConsumeByteSlice()) 306 } 307 308 // AppendString appends a single string of binary data into the buffer. 309 // A string is a uint32 length, followed by that number of raw bytes. 310 func (b *Buffer) AppendString(v string) { 311 b.AppendByteSlice([]byte(v)) 312 } 313 314 // PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian). 315 func (b *Buffer) PutLength(size int) { 316 if len(b.b) < 4 { 317 b.b = append(b.b, make([]byte, 4-len(b.b))...) 318 } 319 320 binary.BigEndian.PutUint32(b.b, uint32(size)) 321 } 322 323 // MarshalBinary returns a clone of the full internal buffer. 324 func (b *Buffer) MarshalBinary() ([]byte, error) { 325 clone := make([]byte, len(b.b)) 326 n := copy(clone, b.b) 327 return clone[:n], nil 328 } 329 330 // UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset. 331 func (b *Buffer) UnmarshalBinary(data []byte) error { 332 if grow := len(data) - len(b.b); grow > 0 { 333 b.b = append(b.b, make([]byte, grow)...) 334 } 335 336 n := copy(b.b, data) 337 b.b = b.b[:n] 338 b.off = 0 339 return nil 340 }