Source File
packets.go
Belonging Package
github.com/pkg/sftp/internal/encoding/ssh/filexfer
package sshfx
import (
)
// smallBufferSize is an initial allocation minimal capacity.
const smallBufferSize = 64
// 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://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
type RawPacket struct {
PacketType PacketType
RequestID uint32
Data Buffer
}
// Type returns the Type field defining the SSH_FXP_xy type for this packet.
func ( *RawPacket) () PacketType {
return .PacketType
}
// 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 ( *RawPacket) () {
.Data = Buffer{}
}
// MarshalPacket returns p as a two-part binary encoding of p.
//
// The internal p.RequestID is overridden by the reqid argument.
func ( *RawPacket) ( uint32, []byte) (, []byte, error) {
:= NewBuffer()
if .Cap() < 9 {
= NewMarshalBuffer(0)
}
.StartPacket(.PacketType, )
return .Packet(.Data.Bytes())
}
// 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 ( *RawPacket) () ([]byte, error) {
return ComposePacket(.MarshalPacket(.RequestID, nil))
}
// UnmarshalFrom decodes a RawPacket from the given Buffer into p.
//
// The Data field will alias the passed in Buffer,
// so the buffer passed in should not be reused before RawPacket.Reset().
func ( *RawPacket) ( *Buffer) error {
* = RawPacket{
PacketType: PacketType(.ConsumeUint8()),
RequestID: .ConsumeUint32(),
}
.Data = *
return .Err
}
// 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.
//
// 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 ( *RawPacket) ( []byte) error {
:= make([]byte, len())
:= copy(, )
return .UnmarshalFrom(NewBuffer([:]))
}
// readPacket reads a uint32 length-prefixed binary data packet from r.
// 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( io.Reader, []byte, uint32) ([]byte, error) {
if cap() < 4 {
// We will need allocate our own buffer just for reading the packet length.
// However, we don’t really want to allocate an extremely narrow buffer (4-bytes),
// and cause unnecessary allocation churn from both length reads and small packet reads,
// so we use smallBufferSize from the bytes package as a reasonable guess.
// But if callers really do want to force narrow throw-away allocation of every packet body,
// they can do so with a buffer of capacity 4.
= make([]byte, smallBufferSize)
}
if , := io.ReadFull(, [:4]); != nil {
return nil,
}
:= unmarshalUint32()
if int() < 5 {
// Must have at least uint8(type) and uint32(request-id)
if int() < 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
}
return nil, ErrShortPacket
}
if > {
return nil, ErrLongPacket
}
if int() > cap() {
// We know int(length) must be positive, because of tests above.
= make([]byte, )
}
, := io.ReadFull(, [:])
return [:],
}
// 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 occasional 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 ( *RawPacket) ( io.Reader, []byte, uint32) error {
, := readPacket(, , )
if != nil {
return
}
return .UnmarshalFrom(NewBuffer())
}
// 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://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
type RequestPacket struct {
RequestID uint32
Request Packet
}
// Type returns the SSH_FXP_xy value associated with the underlying packet.
func ( *RequestPacket) () PacketType {
return .Request.Type()
}
// 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 ( *RequestPacket) () {
.Request = nil
}
// MarshalPacket returns p as a two-part binary encoding of p.
//
// The internal p.RequestID is overridden by the reqid argument.
func ( *RequestPacket) ( uint32, []byte) (, []byte, error) {
if .Request == nil {
return nil, nil, errors.New("empty request packet")
}
return .Request.MarshalPacket(, )
}
// 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 ( *RequestPacket) () ([]byte, error) {
return ComposePacket(.MarshalPacket(.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 ( *RequestPacket) ( *Buffer) error {
:= PacketType(.ConsumeUint8())
if .Err != nil {
return .Err
}
, := newPacketFromType()
if != nil {
return
}
* = RequestPacket{
RequestID: .ConsumeUint32(),
Request: ,
}
return .Request.UnmarshalPacketBody()
}
// 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.
//
// 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 ( *RequestPacket) ( []byte) error {
:= make([]byte, len())
:= copy(, )
return .UnmarshalFrom(NewBuffer([:]))
}
// 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 occasional 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 ( *RequestPacket) ( io.Reader, []byte, uint32) error {
, := readPacket(, , )
if != nil {
return
}
return .UnmarshalFrom(NewBuffer())
}
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |