mirror of https://github.com/pkg/sftp.git
encoding: address slice alias concerns with documentation, and/or cloning
This commit is contained in:
parent
c496ef994c
commit
47ea397efe
|
@ -19,8 +19,8 @@ type Buffer struct {
|
|||
off int
|
||||
}
|
||||
|
||||
// NewBuffer creates and initializes a new Buffer using buf as its initial contents.
|
||||
// The new Buffer takes ownership of buf, and the caller should not use buf after this call.
|
||||
// NewBuffer creates and initializes a new buffer using buf as its initial contents.
|
||||
// The new buffer takes ownership of buf, and the caller should not use buf after this call.
|
||||
//
|
||||
// In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.
|
||||
func NewBuffer(buf []byte) *Buffer {
|
||||
|
@ -35,15 +35,6 @@ func NewMarshalBuffer(size int) *Buffer {
|
|||
return NewBuffer(make([]byte, 4+1+4+size))
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the Buffer to be ready to start marshaling a Packet body into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
b.b = append(b.b[:0], make([]byte, 4)...)
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
}
|
||||
|
||||
// Bytes returns a slice of length b.Len() holding the unconsumed bytes in the Buffer.
|
||||
// The slice is valid for use only until the next buffer modification
|
||||
// (that is, only until the next call to an Append or Consume method).
|
||||
|
@ -51,15 +42,37 @@ func (b *Buffer) Bytes() []byte {
|
|||
return b.b[b.off:]
|
||||
}
|
||||
|
||||
// Packet finalizes the packet started from NewMarshalPacket.
|
||||
// Len returns the number of unconsumed bytes in the buffer.
|
||||
func (b *Buffer) Len() int { return len(b.b) - b.off }
|
||||
|
||||
// Cap returns the capacity of the buffer’s underlying byte slice,
|
||||
// that is, the total space allocated for the buffer’s data.
|
||||
func (b *Buffer) Cap() int { return cap(b.b) }
|
||||
|
||||
// Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
|
||||
func (b *Buffer) Reset() {
|
||||
b.b = b.b[:0]
|
||||
b.off = 0
|
||||
}
|
||||
|
||||
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into.
|
||||
// It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
|
||||
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
|
||||
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0
|
||||
|
||||
b.AppendUint8(uint8(packetType))
|
||||
b.AppendUint32(requestID)
|
||||
}
|
||||
|
||||
// Packet finalizes the packet started from StartPacket.
|
||||
// It is expected that this will end the ownership of the underlying byte-slice,
|
||||
// and so the returned byte-slices may potentially be returned to a memory pool the same as any other byte-slice.
|
||||
// The caller should not use this Buffer at all after this call.
|
||||
// and so the returned byte-slices may be reused the same as any other byte-slice,
|
||||
// the caller should not use this buffer after this call.
|
||||
//
|
||||
// It writes the packet body length into the first four bytes of the Buffer in network byte order (big endian).
|
||||
// The packet body length is the size of the Buffer less the 4-byte length itself, plus the length of payload.
|
||||
// It writes the packet body length into the first four bytes of the buffer in network byte order (big endian).
|
||||
// The packet body length is the length of this buffer less the 4-byte length itself, plus the length of payload.
|
||||
//
|
||||
// It is assumed that no Consume methods have been called on this Buffer,
|
||||
// It is assumed that no Consume methods have been called on this buffer,
|
||||
// and so it returns the whole underlying slice.
|
||||
func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err error) {
|
||||
b.PutLength(len(b.b) - 4 + len(payload))
|
||||
|
@ -67,15 +80,8 @@ func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err err
|
|||
return b.b, payload, nil
|
||||
}
|
||||
|
||||
// Len returns the number of unconsumed bytes in the Buffer.
|
||||
func (b *Buffer) Len() int { return len(b.b) - b.off }
|
||||
|
||||
// Cap returns the capacity of the Buffer’s underlying byte slice,
|
||||
// that is, the total space allocated for the buffer’s data.
|
||||
func (b *Buffer) Cap() int { return cap(b.b) }
|
||||
|
||||
// ConsumeUint8 consumes a single byte from the Buffer.
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeUint8 consumes a single byte from the buffer.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint8() (uint8, error) {
|
||||
if b.Len() < 1 {
|
||||
return 0, ErrShortPacket
|
||||
|
@ -86,13 +92,13 @@ func (b *Buffer) ConsumeUint8() (uint8, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint8 appends a single byte into the Buffer.
|
||||
// AppendUint8 appends a single byte into the buffer.
|
||||
func (b *Buffer) AppendUint8(v uint8) {
|
||||
b.b = append(b.b, v)
|
||||
}
|
||||
|
||||
// ConsumeBool consumes a single byte from the Buffer, and returns true if that byte is non-zero.
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeBool() (bool, error) {
|
||||
v, err := b.ConsumeUint8()
|
||||
if err != nil {
|
||||
|
@ -102,7 +108,7 @@ func (b *Buffer) ConsumeBool() (bool, error) {
|
|||
return v != 0, nil
|
||||
}
|
||||
|
||||
// AppendBool appends a single bool into the Buffer.
|
||||
// AppendBool appends a single bool into the buffer.
|
||||
// It encodes it as a single byte, with false as 0, and true as 1.
|
||||
func (b *Buffer) AppendBool(v bool) {
|
||||
if v {
|
||||
|
@ -112,8 +118,8 @@ func (b *Buffer) AppendBool(v bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// ConsumeUint16 consumes a single uint16 from the Buffer, in network byte order (big-endian).
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint16() (uint16, error) {
|
||||
if b.Len() < 2 {
|
||||
return 0, ErrShortPacket
|
||||
|
@ -124,7 +130,7 @@ func (b *Buffer) ConsumeUint16() (uint16, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint16 appends single uint16 into the Buffer, in network byte order (big-endian).
|
||||
// AppendUint16 appends single uint16 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint16(v uint16) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>8),
|
||||
|
@ -134,13 +140,13 @@ func (b *Buffer) AppendUint16(v uint16) {
|
|||
|
||||
// unmarshalUint32 is used internally to read the packet length.
|
||||
// It is unsafe, and so not exported.
|
||||
// Even in this package, its use should be avoided.
|
||||
// Even within this package, its use should be avoided.
|
||||
func unmarshalUint32(b []byte) uint32 {
|
||||
return binary.BigEndian.Uint32(b)
|
||||
return binary.BigEndian.Uint32(b[:4])
|
||||
}
|
||||
|
||||
// ConsumeUint32 consumes a single uint32 from the Buffer, in network byte order (big-endian).
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint32() (uint32, error) {
|
||||
if b.Len() < 4 {
|
||||
return 0, ErrShortPacket
|
||||
|
@ -151,7 +157,7 @@ func (b *Buffer) ConsumeUint32() (uint32, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint32 appends a single uint32 into the Buffer, in network byte order (big-endian).
|
||||
// AppendUint32 appends a single uint32 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint32(v uint32) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>24),
|
||||
|
@ -161,8 +167,8 @@ func (b *Buffer) AppendUint32(v uint32) {
|
|||
)
|
||||
}
|
||||
|
||||
// ConsumeUint64 consumes a single uint64 from the Buffer, in network byte order (big-endian).
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian).
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeUint64() (uint64, error) {
|
||||
if b.Len() < 8 {
|
||||
return 0, ErrShortPacket
|
||||
|
@ -173,7 +179,7 @@ func (b *Buffer) ConsumeUint64() (uint64, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// AppendUint64 appends a single uint64 into the Buffer, in network byte order (big-endian).
|
||||
// AppendUint64 appends a single uint64 into the buffer, in network byte order (big-endian).
|
||||
func (b *Buffer) AppendUint64(v uint64) {
|
||||
b.b = append(b.b,
|
||||
byte(v>>56),
|
||||
|
@ -187,8 +193,8 @@ func (b *Buffer) AppendUint64(v uint64) {
|
|||
)
|
||||
}
|
||||
|
||||
// ConsumeInt64 consumes a single int64 from the Buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If Buffer does not have enough data, it will return ErrShortPacket.
|
||||
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with two’s complement.
|
||||
// If the buffer does not have enough data, it will return ErrShortPacket.
|
||||
func (b *Buffer) ConsumeInt64() (int64, error) {
|
||||
u, err := b.ConsumeUint64()
|
||||
if err != nil {
|
||||
|
@ -198,14 +204,20 @@ func (b *Buffer) ConsumeInt64() (int64, error) {
|
|||
return int64(u), err
|
||||
}
|
||||
|
||||
// AppendInt64 appends a single uint64 into the Buffer, in network byte order (big-endian) with two’s complement.
|
||||
func (b *Buffer) AppendInt64(v uint64) {
|
||||
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s complement.
|
||||
func (b *Buffer) AppendInt64(v int64) {
|
||||
b.AppendUint64(uint64(v))
|
||||
}
|
||||
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the Buffer.
|
||||
// ConsumeByteSlice consumes a single string of raw binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If Buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused
|
||||
// (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
|
||||
//
|
||||
// In no case will any Consume calls return overlapping slice aliases,
|
||||
// and Append calls are guaranteed to not disturb this slice alias.
|
||||
func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
|
||||
length, err := b.ConsumeUint32()
|
||||
if err != nil {
|
||||
|
@ -224,16 +236,16 @@ func (b *Buffer) ConsumeByteSlice() ([]byte, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// AppendByteSlice appends a single string of raw binary data into the Buffer.
|
||||
// AppendByteSlice appends a single string of raw binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendByteSlice(v []byte) {
|
||||
b.AppendUint32(uint32(len(v)))
|
||||
b.b = append(b.b, v...)
|
||||
}
|
||||
|
||||
// ConsumeString consumes a single string of binary data from the Buffer.
|
||||
// ConsumeString consumes a single string of binary data from the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
// If Buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
// If the buffer does not have enough data, or defines a length larger than available, it will return ErrShortPacket.
|
||||
//
|
||||
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
|
||||
// All caveats on using arbitrary binary data in Go strings applies.
|
||||
|
@ -246,13 +258,13 @@ func (b *Buffer) ConsumeString() (string, error) {
|
|||
return string(v), nil
|
||||
}
|
||||
|
||||
// AppendString appends a single string of binary data into the Buffer.
|
||||
// AppendString appends a single string of binary data into the buffer.
|
||||
// A string is a uint32 length, followed by that number of raw bytes.
|
||||
func (b *Buffer) AppendString(v string) {
|
||||
b.AppendByteSlice([]byte(v))
|
||||
}
|
||||
|
||||
// PutLength writes the given size into the first four bytes of the Buffer in network byte order (big endian).
|
||||
// PutLength writes the given size into the first four bytes of the buffer in network byte order (big endian).
|
||||
func (b *Buffer) PutLength(size int) {
|
||||
if len(b.b) < 4 {
|
||||
b.b = append(b.b, make([]byte, 4-len(b.b))...)
|
||||
|
@ -261,19 +273,21 @@ func (b *Buffer) PutLength(size int) {
|
|||
binary.BigEndian.PutUint32(b.b, uint32(size))
|
||||
}
|
||||
|
||||
// MarshalBinary returns the remaining binary data in the Buffer as a byte slice.
|
||||
// This aliases the internal buffer, and so comes with the same caveats as Bytes().
|
||||
//
|
||||
// This function is a thin wrapper of Bytes() solely to implement encoding.BinaryMarshaler.
|
||||
// MarshalBinary returns a clone of the full internal buffer.
|
||||
func (b *Buffer) MarshalBinary() ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
clone := make([]byte, len(b.b))
|
||||
n := copy(clone, b.b)
|
||||
return clone[:n], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary sets the internal buffer of b to be data, and zeros any internal offset.
|
||||
// To avoid additional allocations,
|
||||
// UnmarshalBinary takes ownership of buf, and the caller should not use buf after this call.
|
||||
// UnmarshalBinary sets the internal buffer of b to be a clone of data, and zeros the internal offset.
|
||||
func (b *Buffer) UnmarshalBinary(data []byte) error {
|
||||
b.b = data
|
||||
if grow := len(data) - len(b.b); grow > 0 {
|
||||
b.b = append(b.b, make([]byte, grow)...)
|
||||
}
|
||||
|
||||
n := copy(b.b, data)
|
||||
b.b = b.b[:n]
|
||||
b.off = 0
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -53,6 +53,13 @@ func newPacketFromType(typ PacketType) (Packet, error) {
|
|||
|
||||
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
//
|
||||
// RawPacket is intended for use in clients receiving responses,
|
||||
// where a response will be expected to be of a limited number of types,
|
||||
// and unmarshaling unknown/unexpected response packets is unnecessary.
|
||||
//
|
||||
// For servers expecting to receive arbitrary request packet types,
|
||||
// use RequestPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RawPacket struct {
|
||||
Type PacketType
|
||||
|
@ -61,6 +68,13 @@ type RawPacket struct {
|
|||
Data Buffer
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables of RawPacket,
|
||||
// releasing underlying resources, and making them and the RawPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RawPacket) Reset() {
|
||||
p.Data = Buffer{}
|
||||
}
|
||||
|
||||
// MarshalPacket returns p as a two-part binary encoding of p.
|
||||
//
|
||||
// The internal p.RequestID is overridden by the reqid argument.
|
||||
|
@ -76,14 +90,17 @@ func (p *RawPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byt
|
|||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RawPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
|
||||
//
|
||||
// The Data field will take ownership of the underyling byte slice of buf.
|
||||
// The caller should not use buf after this call.
|
||||
// The Data field will alias the passed in Buffer,
|
||||
// so the buffer passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
|
@ -103,16 +120,27 @@ func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
|
|||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// NOTE: To avoid extra allocations, UnmarshalBinary aliases the given byte slice.
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Data is not allowed to alias any part of the data byte slice.
|
||||
func (p *RawPacket) UnmarshalBinary(data []byte) error {
|
||||
return p.UnmarshalFrom(NewBuffer(data))
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// readPacket reads a uint32 length-prefixed binary data packet from r.
|
||||
// If the given buffer is less than 4-bytes, it allocates a new buffer of size DefaultMaxPacketLength.
|
||||
func readPacket(r io.Reader, b []byte) ([]byte, error) {
|
||||
if len(b) < 4 {
|
||||
b = make([]byte, DefaultMaxPacketLength)
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// If the packet length read from r is bigger than maxPacketLength,
|
||||
// or greater than math.MaxInt32 on a 32-bit implementation,
|
||||
// then a `ErrLongPacket` error will be returned.
|
||||
//
|
||||
// If the given byte slice is insufficient to hold the packet,
|
||||
// then it will be extended to fill the packet size.
|
||||
func readPacket(r io.Reader, b []byte, maxPacketLength uint32) ([]byte, error) {
|
||||
if len(b) < 64 {
|
||||
b = make([]byte, 64)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, b[:4]); err != nil {
|
||||
|
@ -120,45 +148,65 @@ func readPacket(r io.Reader, b []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
length := unmarshalUint32(b)
|
||||
if length < 1 {
|
||||
if int(length) < 5 {
|
||||
if int(length) < 0 {
|
||||
// Only possible when strconv.IntSize == 32,
|
||||
// the packet length is longer than math.MaxInt32,
|
||||
// and thus longer than any possible slice.
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
// Must have at least uint8(type) and uint32(request-id)
|
||||
return nil, ErrShortPacket
|
||||
}
|
||||
if length > uint32(len(b)) {
|
||||
if length > maxPacketLength {
|
||||
return nil, ErrLongPacket
|
||||
}
|
||||
|
||||
if int(length) > cap(b) {
|
||||
// We know int(length) must be positive, because of tests above.
|
||||
b = make([]byte, length)
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(r, b[:length])
|
||||
return b[:n], err
|
||||
}
|
||||
|
||||
// ReadFrom reads a full raw packet out of the given reader.
|
||||
func (p *RawPacket) ReadFrom(r io.Reader, b []byte) error {
|
||||
b, err := readPacket(r, b)
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occassional large packets.
|
||||
//
|
||||
// The Data field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalBinary(b)
|
||||
}
|
||||
|
||||
// RequestPacket decodes a full RequestPacket from the internal Data based on the Type.
|
||||
// Prefer using a RequestPacket directly, rather than going indirectly through RawPacket.
|
||||
func (p *RawPacket) RequestPacket() (*RequestPacket, error) {
|
||||
packet, err := newPacketFromType(p.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packet.UnmarshalPacketBody(&p.Data)
|
||||
|
||||
return &RequestPacket{
|
||||
RequestID: p.RequestID,
|
||||
|
||||
Request: packet,
|
||||
}, nil
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
|
||||
// RequestPacket implements the general packet format from draft-ietf-secsh-filexfer-02
|
||||
// but also automatically decode/encodes valid request packets (2 < type < 100 || type == 200).
|
||||
//
|
||||
// RequestPacket is intended for use in servers receiving requests,
|
||||
// where any arbitrary request may be received, and so decoding them automatically
|
||||
// is useful.
|
||||
//
|
||||
// For clients expecting to receive specific response packet types,
|
||||
// where automatic unmarshaling of the packet body does not make sense,
|
||||
// use RawPacket.
|
||||
//
|
||||
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3
|
||||
type RequestPacket struct {
|
||||
|
@ -167,8 +215,9 @@ type RequestPacket struct {
|
|||
Request Packet
|
||||
}
|
||||
|
||||
// Reset clears the pointers and reference-semantic variables of RequestPacket,
|
||||
// making it suitable to be put into a sync.Pool.
|
||||
// Reset clears the pointers and reference-semantic variables in RequestPacket,
|
||||
// releasing underlying resources, and making them and the RequestPacket suitable to be reused,
|
||||
// so long as no other references have been kept.
|
||||
func (p *RequestPacket) Reset() {
|
||||
p.Request = nil
|
||||
}
|
||||
|
@ -185,11 +234,17 @@ func (p *RequestPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
|
|||
}
|
||||
|
||||
// MarshalBinary returns p as the binary encoding of p.
|
||||
//
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because it is inefficient with allocations.
|
||||
func (p *RequestPacket) MarshalBinary() ([]byte, error) {
|
||||
return ComposePacket(p.MarshalPacket(p.RequestID, nil))
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes a RequestPacket from the given Buffer into p.
|
||||
//
|
||||
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE),
|
||||
// so the buffer passed in should not be reused before RequestPacket.Reset().
|
||||
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
||||
typ, err := buf.ConsumeUint8()
|
||||
if err != nil {
|
||||
|
@ -208,20 +263,39 @@ func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
|
|||
return p.Request.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a full raw packet out of the given data.
|
||||
// UnmarshalBinary decodes a full request packet out of the given data.
|
||||
// It is assumed that the uint32(length) has already been consumed to receive the data.
|
||||
//
|
||||
// NOTE: To avoid extra allocations, UnmarshalBinary aliases the given byte slice.
|
||||
// This is a convenience implementation primarily intended for tests,
|
||||
// because this must clone the given data byte slice,
|
||||
// as Request is not allowed to alias any part of the data byte slice.
|
||||
func (p *RequestPacket) UnmarshalBinary(data []byte) error {
|
||||
return p.UnmarshalFrom(NewBuffer(data))
|
||||
clone := make([]byte, len(data))
|
||||
n := copy(clone, data)
|
||||
return p.UnmarshalFrom(NewBuffer(clone[:n]))
|
||||
}
|
||||
|
||||
// ReadFrom reads a full raw packet out of the given reader.
|
||||
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte) error {
|
||||
b, err := readPacket(r, b)
|
||||
// ReadFrom provides a simple functional packet reader,
|
||||
// using the given byte slice as a backing array.
|
||||
//
|
||||
// To protect against potential denial of service attacks,
|
||||
// if the read packet length is longer than maxPacketLength,
|
||||
// then no packet data will be read, and ErrLongPacket will be returned.
|
||||
// (On 32-bit int architectures, all packets >= 2^31 in length
|
||||
// will return ErrLongPacket regardless of maxPacketLength.)
|
||||
//
|
||||
// If the read packet length is longer than cap(b),
|
||||
// then a throw-away slice will allocated to meet the exact packet length.
|
||||
// This can be used to limit the length of reused buffers,
|
||||
// while still allowing reception of occassional large packets.
|
||||
//
|
||||
// The Request field may alias the passed in byte slice,
|
||||
// so the byte slice passed in should not be reused before RawPacket.Reset().
|
||||
func (p *RequestPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) error {
|
||||
b, err := readPacket(r, b, maxPacketLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.UnmarshalBinary(b)
|
||||
return p.UnmarshalFrom(NewBuffer(b))
|
||||
}
|
||||
|
|
|
@ -7,15 +7,20 @@ import (
|
|||
|
||||
func TestRawPacket(t *testing.T) {
|
||||
const (
|
||||
id = 42
|
||||
path = "foo"
|
||||
id = 42
|
||||
errMsg = "eof"
|
||||
langTag = "en"
|
||||
)
|
||||
|
||||
p := &RawPacket{
|
||||
Type: PacketTypeStat,
|
||||
Type: PacketTypeStatus,
|
||||
RequestID: id,
|
||||
Data: Buffer{
|
||||
b: []byte{0x00, 0x00, 0x00, 0x03, 'f', 'o', 'o'},
|
||||
b: []byte{
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x03, 'e', 'o', 'f',
|
||||
0x00, 0x00, 0x00, 0x02, 'e', 'n',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -25,10 +30,12 @@ func TestRawPacket(t *testing.T) {
|
|||
}
|
||||
|
||||
want := []byte{
|
||||
0x00, 0x00, 0x00, 12,
|
||||
17,
|
||||
0x00, 0x00, 0x00, 22,
|
||||
101,
|
||||
0x00, 0x00, 0x00, 42,
|
||||
0x00, 0x00, 0x00, 3, 'f', 'o', 'o',
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 3, 'e', 'o', 'f',
|
||||
0x00, 0x00, 0x00, 2, 'e', 'n',
|
||||
}
|
||||
|
||||
if !bytes.Equal(data, want) {
|
||||
|
@ -37,11 +44,11 @@ func TestRawPacket(t *testing.T) {
|
|||
|
||||
*p = RawPacket{}
|
||||
|
||||
if err := p.ReadFrom(bytes.NewReader(data), nil); err != nil {
|
||||
if err := p.ReadFrom(bytes.NewReader(data), nil, DefaultMaxPacketLength); err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
|
||||
if p.Type != PacketTypeStat {
|
||||
if p.Type != PacketTypeStatus {
|
||||
t.Errorf("RawPacket.UnmarshalBinary(): Type was %v, but expected %v", p.Type, PacketTypeStat)
|
||||
}
|
||||
|
||||
|
@ -50,29 +57,28 @@ func TestRawPacket(t *testing.T) {
|
|||
}
|
||||
|
||||
want = []byte{
|
||||
0x00, 0x00, 0x00, 3, 'f', 'o', 'o',
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 3, 'e', 'o', 'f',
|
||||
0x00, 0x00, 0x00, 2, 'e', 'n',
|
||||
}
|
||||
|
||||
if !bytes.Equal(p.Data.Bytes(), want) {
|
||||
t.Errorf("RawPacket.UnmarshalBinary(): Data was %X, but expected %X", p.Data, want)
|
||||
t.Fatalf("RawPacket.UnmarshalBinary(): Data was %X, but expected %X", p.Data, want)
|
||||
}
|
||||
|
||||
rp, err := p.RequestPacket()
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
var resp StatusPacket
|
||||
resp.UnmarshalPacketBody(&p.Data)
|
||||
|
||||
if resp.StatusCode != StatusEOF {
|
||||
t.Errorf("UnmarshalPacketBody(RawPacket.Data): StatusCode was %v, but expected %v", resp.StatusCode, StatusEOF)
|
||||
}
|
||||
|
||||
if rp.RequestID != uint32(id) {
|
||||
t.Errorf("RawPacket.RequestPacket(): RequestID was %d, but expected %d", rp.RequestID, id)
|
||||
if resp.ErrorMessage != errMsg {
|
||||
t.Errorf("UnmarshalPacketBody(RawPacket.Data): ErrorMessage was %q, but expected %q", resp.ErrorMessage, errMsg)
|
||||
}
|
||||
|
||||
req, ok := rp.Request.(*StatPacket)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected Request type was %T, but expected %T", rp.Request, req)
|
||||
}
|
||||
|
||||
if req.Path != path {
|
||||
t.Errorf("RawPacket.RequestPacket(): Request.Path was %q, but expected %q", req.Path, path)
|
||||
if resp.LanguageTag != langTag {
|
||||
t.Errorf("UnmarshalPacketBody(RawPacket.Data): LanguageTag was %q, but expected %q", resp.LanguageTag, langTag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +113,7 @@ func TestRequestPacket(t *testing.T) {
|
|||
|
||||
*p = RequestPacket{}
|
||||
|
||||
if err := p.ReadFrom(bytes.NewReader(data), nil); err != nil {
|
||||
if err := p.ReadFrom(bytes.NewReader(data), nil, DefaultMaxPacketLength); err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue