refactor sshfx encoding, fix link rot, go fmt

This commit is contained in:
Cassondra Foesch 2023-03-27 17:05:24 +00:00
parent 971c283182
commit bd61319b07
53 changed files with 452 additions and 491 deletions

View File

@ -1,7 +1,7 @@
package sftp package sftp
// ssh_FXP_ATTRS support // ssh_FXP_ATTRS support
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5 // see https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
import ( import (
"os" "os"

View File

@ -1,3 +1,4 @@
//go:build plan9 || windows || android
// +build plan9 windows android // +build plan9 windows android
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || aix || js
// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js
package sftp package sftp

View File

@ -259,11 +259,11 @@ func (c *Client) Create(path string) (*File, error) {
return c.open(path, flags(os.O_RDWR|os.O_CREATE|os.O_TRUNC)) return c.open(path, flags(os.O_RDWR|os.O_CREATE|os.O_TRUNC))
} }
const sftpProtocolVersion = 3 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 const sftpProtocolVersion = 3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
func (c *Client) sendInit() error { func (c *Client) sendInit() error {
return c.clientConn.conn.sendPacket(&sshFxInitPacket{ return c.clientConn.conn.sendPacket(&sshFxInitPacket{
Version: sftpProtocolVersion, // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 Version: sftpProtocolVersion, // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
}) })
} }

View File

@ -1,3 +1,4 @@
//go:build debug
// +build debug // +build debug
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
package sftp package sftp

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// Attributes related flags. // Attributes related flags.
const ( const (
@ -12,7 +12,7 @@ const (
// Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02 // Attributes defines the file attributes type defined in draft-ietf-secsh-filexfer-02
// //
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5 // Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
type Attributes struct { type Attributes struct {
Flags uint32 Flags uint32
@ -116,32 +116,32 @@ func (a *Attributes) Len() int {
} }
// MarshalInto marshals e onto the end of the given Buffer. // MarshalInto marshals e onto the end of the given Buffer.
func (a *Attributes) MarshalInto(b *Buffer) { func (a *Attributes) MarshalInto(buf *Buffer) {
b.AppendUint32(a.Flags) buf.AppendUint32(a.Flags)
if a.Flags&AttrSize != 0 { if a.Flags&AttrSize != 0 {
b.AppendUint64(a.Size) buf.AppendUint64(a.Size)
} }
if a.Flags&AttrUIDGID != 0 { if a.Flags&AttrUIDGID != 0 {
b.AppendUint32(a.UID) buf.AppendUint32(a.UID)
b.AppendUint32(a.GID) buf.AppendUint32(a.GID)
} }
if a.Flags&AttrPermissions != 0 { if a.Flags&AttrPermissions != 0 {
b.AppendUint32(uint32(a.Permissions)) buf.AppendUint32(uint32(a.Permissions))
} }
if a.Flags&AttrACModTime != 0 { if a.Flags&AttrACModTime != 0 {
b.AppendUint32(a.ATime) buf.AppendUint32(a.ATime)
b.AppendUint32(a.MTime) buf.AppendUint32(a.MTime)
} }
if a.Flags&AttrExtended != 0 { if a.Flags&AttrExtended != 0 {
b.AppendUint32(uint32(len(a.ExtendedAttributes))) buf.AppendUint32(uint32(len(a.ExtendedAttributes)))
for _, ext := range a.ExtendedAttributes { for _, ext := range a.ExtendedAttributes {
ext.MarshalInto(b) ext.MarshalInto(buf)
} }
} }
} }
@ -156,74 +156,51 @@ func (a *Attributes) MarshalBinary() ([]byte, error) {
// UnmarshalFrom unmarshals an Attributes from the given Buffer into e. // UnmarshalFrom unmarshals an Attributes from the given Buffer into e.
// //
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined. // NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
func (a *Attributes) UnmarshalFrom(b *Buffer) (err error) { func (a *Attributes) UnmarshalFrom(buf *Buffer) (err error) {
flags, err := b.ConsumeUint32() flags := buf.ConsumeUint32()
if err != nil {
return err
}
return a.XXX_UnmarshalByFlags(flags, b) return a.XXX_UnmarshalByFlags(flags, buf)
} }
// XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode. // XXX_UnmarshalByFlags uses the pre-existing a.Flags field to determine which fields to decode.
// DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp. // DO NOT USE THIS: it is an anti-corruption function to implement existing internal usage in pkg/sftp.
// This function is not a part of any compatibility promise. // This function is not a part of any compatibility promise.
func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, b *Buffer) (err error) { func (a *Attributes) XXX_UnmarshalByFlags(flags uint32, buf *Buffer) (err error) {
a.Flags = flags a.Flags = flags
// Short-circuit dummy attributes. // Short-circuit dummy attributes.
if a.Flags == 0 { if a.Flags == 0 {
return nil return buf.Err
} }
if a.Flags&AttrSize != 0 { if a.Flags&AttrSize != 0 {
if a.Size, err = b.ConsumeUint64(); err != nil { a.Size = buf.ConsumeUint64()
return err
}
} }
if a.Flags&AttrUIDGID != 0 { if a.Flags&AttrUIDGID != 0 {
if a.UID, err = b.ConsumeUint32(); err != nil { a.UID = buf.ConsumeUint32()
return err a.GID = buf.ConsumeUint32()
}
if a.GID, err = b.ConsumeUint32(); err != nil {
return err
}
} }
if a.Flags&AttrPermissions != 0 { if a.Flags&AttrPermissions != 0 {
m, err := b.ConsumeUint32() a.Permissions = FileMode(buf.ConsumeUint32())
if err != nil {
return err
}
a.Permissions = FileMode(m)
} }
if a.Flags&AttrACModTime != 0 { if a.Flags&AttrACModTime != 0 {
if a.ATime, err = b.ConsumeUint32(); err != nil { a.ATime = buf.ConsumeUint32()
return err a.MTime = buf.ConsumeUint32()
}
if a.MTime, err = b.ConsumeUint32(); err != nil {
return err
}
} }
if a.Flags&AttrExtended != 0 { if a.Flags&AttrExtended != 0 {
count, err := b.ConsumeUint32() count := buf.ConsumeCount()
if err != nil {
return err
}
a.ExtendedAttributes = make([]ExtendedAttribute, count) a.ExtendedAttributes = make([]ExtendedAttribute, count)
for i := range a.ExtendedAttributes { for i := range a.ExtendedAttributes {
a.ExtendedAttributes[i].UnmarshalFrom(b) a.ExtendedAttributes[i].UnmarshalFrom(buf)
} }
} }
return nil return buf.Err
} }
// UnmarshalBinary decodes the binary encoding of Attributes into e. // UnmarshalBinary decodes the binary encoding of Attributes into e.
@ -233,7 +210,7 @@ func (a *Attributes) UnmarshalBinary(data []byte) error {
// ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02 // ExtendedAttribute defines the extended file attribute type defined in draft-ietf-secsh-filexfer-02
// //
// Defined in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5 // Defined in: https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-5
type ExtendedAttribute struct { type ExtendedAttribute struct {
Type string Type string
Data string Data string
@ -245,9 +222,9 @@ func (e *ExtendedAttribute) Len() int {
} }
// MarshalInto marshals e onto the end of the given Buffer. // MarshalInto marshals e onto the end of the given Buffer.
func (e *ExtendedAttribute) MarshalInto(b *Buffer) { func (e *ExtendedAttribute) MarshalInto(buf *Buffer) {
b.AppendString(e.Type) buf.AppendString(e.Type)
b.AppendString(e.Data) buf.AppendString(e.Data)
} }
// MarshalBinary returns e as the binary encoding of e. // MarshalBinary returns e as the binary encoding of e.
@ -258,16 +235,13 @@ func (e *ExtendedAttribute) MarshalBinary() ([]byte, error) {
} }
// UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e. // UnmarshalFrom unmarshals an ExtendedAattribute from the given Buffer into e.
func (e *ExtendedAttribute) UnmarshalFrom(b *Buffer) (err error) { func (e *ExtendedAttribute) UnmarshalFrom(buf *Buffer) (err error) {
if e.Type, err = b.ConsumeString(); err != nil { *e = ExtendedAttribute{
return err Type: buf.ConsumeString(),
Data: buf.ConsumeString(),
} }
if e.Data, err = b.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e. // UnmarshalBinary decodes the binary encoding of ExtendedAttribute into e.
@ -290,11 +264,11 @@ func (e *NameEntry) Len() int {
} }
// MarshalInto marshals e onto the end of the given Buffer. // MarshalInto marshals e onto the end of the given Buffer.
func (e *NameEntry) MarshalInto(b *Buffer) { func (e *NameEntry) MarshalInto(buf *Buffer) {
b.AppendString(e.Filename) buf.AppendString(e.Filename)
b.AppendString(e.Longname) buf.AppendString(e.Longname)
e.Attrs.MarshalInto(b) e.Attrs.MarshalInto(buf)
} }
// MarshalBinary returns e as the binary encoding of e. // MarshalBinary returns e as the binary encoding of e.
@ -307,16 +281,13 @@ func (e *NameEntry) MarshalBinary() ([]byte, error) {
// UnmarshalFrom unmarshals an NameEntry from the given Buffer into e. // UnmarshalFrom unmarshals an NameEntry from the given Buffer into e.
// //
// NOTE: The values of fields not covered in the a.Flags are explicitly undefined. // NOTE: The values of fields not covered in the a.Flags are explicitly undefined.
func (e *NameEntry) UnmarshalFrom(b *Buffer) (err error) { func (e *NameEntry) UnmarshalFrom(buf *Buffer) (err error) {
if e.Filename, err = b.ConsumeString(); err != nil { *e = NameEntry{
return err Filename: buf.ConsumeString(),
Longname: buf.ConsumeString(),
} }
if e.Longname, err = b.ConsumeString(); err != nil { return e.Attrs.UnmarshalFrom(buf)
return err
}
return e.Attrs.UnmarshalFrom(b)
} }
// UnmarshalBinary decodes the binary encoding of NameEntry into e. // UnmarshalBinary decodes the binary encoding of NameEntry into e.

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"encoding/binary" "encoding/binary"
@ -17,6 +17,7 @@ var (
type Buffer struct { type Buffer struct {
b []byte b []byte
off int off int
Err error
} }
// NewBuffer creates and initializes a new buffer using buf as its initial contents. // NewBuffer creates and initializes a new buffer using buf as its initial contents.
@ -51,14 +52,17 @@ 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. // Reset resets the buffer to be empty, but it retains the underlying storage for use by future Appends.
func (b *Buffer) Reset() { func (b *Buffer) Reset() {
b.b = b.b[:0] *b = Buffer{
b.off = 0 b: b.b[:0],
}
} }
// StartPacket resets and initializes the buffer to be ready to start marshaling a packet into. // 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. // It truncates the buffer, reserves space for uint32(length), then appends the given packetType and requestID.
func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) { func (b *Buffer) StartPacket(packetType PacketType, requestID uint32) {
b.b, b.off = append(b.b[:0], make([]byte, 4)...), 0 *b = Buffer{
b: append(b.b[:0], make([]byte, 4)...),
}
b.AppendUint8(uint8(packetType)) b.AppendUint8(uint8(packetType))
b.AppendUint32(requestID) b.AppendUint32(requestID)
@ -81,15 +85,21 @@ func (b *Buffer) Packet(payload []byte) (header, payloadPassThru []byte, err err
} }
// ConsumeUint8 consumes a single byte from the buffer. // ConsumeUint8 consumes a single byte from the buffer.
// If the buffer does not have enough data, it will return ErrShortPacket. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeUint8() (uint8, error) { func (b *Buffer) ConsumeUint8() uint8 {
if b.Err != nil {
return 0
}
if b.Len() < 1 { if b.Len() < 1 {
return 0, ErrShortPacket b.off = len(b.b)
b.Err = ErrShortPacket
return 0
} }
var v uint8 var v uint8
v, b.off = b.b[b.off], b.off+1 v, b.off = b.b[b.off], b.off+1
return v, nil return v
} }
// AppendUint8 appends a single byte into the buffer. // AppendUint8 appends a single byte into the buffer.
@ -98,14 +108,9 @@ func (b *Buffer) AppendUint8(v uint8) {
} }
// ConsumeBool consumes a single byte from the buffer, and returns true if that byte is non-zero. // 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. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeBool() (bool, error) { func (b *Buffer) ConsumeBool() bool {
v, err := b.ConsumeUint8() return b.ConsumeUint8() != 0
if err != nil {
return false, err
}
return v != 0, nil
} }
// AppendBool appends a single bool into the buffer. // AppendBool appends a single bool into the buffer.
@ -119,15 +124,21 @@ func (b *Buffer) AppendBool(v bool) {
} }
// ConsumeUint16 consumes a single uint16 from the buffer, in network byte order (big-endian). // 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. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeUint16() (uint16, error) { func (b *Buffer) ConsumeUint16() uint16 {
if b.Err != nil {
return 0
}
if b.Len() < 2 { if b.Len() < 2 {
return 0, ErrShortPacket b.off = len(b.b)
b.Err = ErrShortPacket
return 0
} }
v := binary.BigEndian.Uint16(b.b[b.off:]) v := binary.BigEndian.Uint16(b.b[b.off:])
b.off += 2 b.off += 2
return v, nil return v
} }
// 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).
@ -146,15 +157,21 @@ func unmarshalUint32(b []byte) uint32 {
} }
// ConsumeUint32 consumes a single uint32 from the buffer, in network byte order (big-endian). // 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. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeUint32() (uint32, error) { func (b *Buffer) ConsumeUint32() uint32 {
if b.Err != nil {
return 0
}
if b.Len() < 4 { if b.Len() < 4 {
return 0, ErrShortPacket b.off = len(b.b)
b.Err = ErrShortPacket
return 0
} }
v := binary.BigEndian.Uint32(b.b[b.off:]) v := binary.BigEndian.Uint32(b.b[b.off:])
b.off += 4 b.off += 4
return v, nil return v
} }
// 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).
@ -167,16 +184,33 @@ func (b *Buffer) AppendUint32(v uint32) {
) )
} }
// ConsumeCount consumes a single uint32 count from the buffer, in network byte order (big-endian) as an int.
// If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeCount() int {
return int(b.ConsumeUint32())
}
// AppendCount appends a single int length as a uint32 into the buffer, in network byte order (big-endian).
func (b *Buffer) AppendCount(v int) {
b.AppendUint32(uint32(v))
}
// ConsumeUint64 consumes a single uint64 from the buffer, in network byte order (big-endian). // 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. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeUint64() (uint64, error) { func (b *Buffer) ConsumeUint64() uint64 {
if b.Err != nil {
return 0
}
if b.Len() < 8 { if b.Len() < 8 {
return 0, ErrShortPacket b.off = len(b.b)
b.Err = ErrShortPacket
return 0
} }
v := binary.BigEndian.Uint64(b.b[b.off:]) v := binary.BigEndian.Uint64(b.b[b.off:])
b.off += 8 b.off += 8
return v, nil return v
} }
// 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).
@ -194,14 +228,9 @@ func (b *Buffer) AppendUint64(v uint64) {
} }
// ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with twos complement. // ConsumeInt64 consumes a single int64 from the buffer, in network byte order (big-endian) with twos complement.
// If the buffer does not have enough data, it will return ErrShortPacket. // If the buffer does not have enough data, it will set Err to ErrShortPacket.
func (b *Buffer) ConsumeInt64() (int64, error) { func (b *Buffer) ConsumeInt64() int64 {
u, err := b.ConsumeUint64() return int64(b.ConsumeUint64())
if err != nil {
return 0, err
}
return int64(u), err
} }
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with twos complement. // AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with twos complement.
@ -211,29 +240,52 @@ func (b *Buffer) AppendInt64(v int64) {
// 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. // A string is a uint32 length, followed by that number of raw bytes.
// If the 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 set Err to ErrShortPacket.
// //
// The returned slice aliases the buffer contents, and is valid only as long as the buffer is not reused // 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). // (that is, only until the next call to Reset, PutLength, StartPacket, or UnmarshalBinary).
// //
// In no case will any Consume calls return overlapping slice aliases, // In no case will any Consume calls return overlapping slice aliases,
// and Append calls are guaranteed to not disturb this slice alias. // and Append calls are guaranteed to not disturb this slice alias.
func (b *Buffer) ConsumeByteSlice() ([]byte, error) { func (b *Buffer) ConsumeByteSlice() []byte {
length, err := b.ConsumeUint32() length := int(b.ConsumeUint32())
if err != nil { if b.Err != nil {
return nil, err return nil
} }
if b.Len() < int(length) { if b.Len() < length || length < 0 {
return nil, ErrShortPacket b.off = len(b.b)
b.Err = ErrShortPacket
return nil
} }
v := b.b[b.off:] v := b.b[b.off:]
if len(v) > int(length) { if len(v) > length || cap(v) > length {
v = v[:length:length] v = v[:length:length]
} }
b.off += int(length) b.off += int(length)
return v, nil return v
}
// ConsumeByteSliceCopy consumes a single string of raw binary data as a copy from the buffer.
// A string is a uint32 length, followed by that number of raw bytes.
// If the buffer does not have enough data, or defines a length larger than available, it will set Err to ErrShortPacket.
//
// The returned slice does not alias any buffer contents,
// and will therefore be valid even if the buffer is later reused.
//
// If hint has sufficient capacity to hold the data, it will be reused and overwritten,
// otherwise a new backing slice will be allocated and returned.
func (b *Buffer) ConsumeByteSliceCopy(hint []byte) []byte {
data := b.ConsumeByteSlice()
if grow := len(data) - len(hint); grow > 0 {
hint = append(hint, make([]byte, grow)...)
}
n := copy(hint, data)
hint = hint[:n]
return hint
} }
// AppendByteSlice appends a single string of raw binary data into the buffer. // AppendByteSlice appends a single string of raw binary data into the buffer.
@ -245,17 +297,12 @@ func (b *Buffer) AppendByteSlice(v []byte) {
// 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. // A string is a uint32 length, followed by that number of raw bytes.
// If the 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 set Err to ErrShortPacket.
// //
// NOTE: Go implicitly assumes that strings contain UTF-8 encoded data. // NOTE: Go implicitly assumes that strings contain UTF-8 encoded data.
// All caveats on using arbitrary binary data in Go strings applies. // All caveats on using arbitrary binary data in Go strings applies.
func (b *Buffer) ConsumeString() (string, error) { func (b *Buffer) ConsumeString() string {
v, err := b.ConsumeByteSlice() return string(b.ConsumeByteSlice())
if err != nil {
return "", err
}
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.

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"encoding" "encoding"
@ -86,8 +86,9 @@ func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
// If the extension has not been registered, then a new Buffer will be allocated. // If the extension has not been registered, then a new Buffer will be allocated.
// Then the request-specific-data will be unmarshaled from the rest of the buffer. // Then the request-specific-data will be unmarshaled from the rest of the buffer.
func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.ExtendedRequest, err = buf.ConsumeString(); err != nil { p.ExtendedRequest = buf.ConsumeString()
return err if buf.Err != nil {
return buf.Err
} }
if p.Data == nil { if p.Data == nil {

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"
@ -20,9 +20,9 @@ func (d *testExtendedData) MarshalBinary() ([]byte, error) {
func (d *testExtendedData) UnmarshalBinary(data []byte) error { func (d *testExtendedData) UnmarshalBinary(data []byte) error {
buf := NewBuffer(data) buf := NewBuffer(data)
v, err := buf.ConsumeUint8() v := buf.ConsumeUint8()
if err != nil { if buf.Err != nil {
return err return buf.Err
} }
d.value = v ^ 0x2a d.value = v ^ 0x2a

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13. // ExtensionPair defines the extension-pair type defined in draft-ietf-secsh-filexfer-13.
// This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions. // This type is backwards-compatible with how draft-ietf-secsh-filexfer-02 defines extensions.
@ -29,15 +29,12 @@ func (e *ExtensionPair) MarshalBinary() ([]byte, error) {
// UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e. // UnmarshalFrom unmarshals an ExtensionPair from the given Buffer into e.
func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) { func (e *ExtensionPair) UnmarshalFrom(buf *Buffer) (err error) {
if e.Name, err = buf.ConsumeString(); err != nil { *e = ExtensionPair{
return err Name: buf.ConsumeString(),
Data: buf.ConsumeString(),
} }
if e.Data, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// UnmarshalBinary decodes the binary encoding of ExtensionPair into e. // UnmarshalBinary decodes the binary encoding of ExtensionPair into e.

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -1,5 +1,5 @@
// Package filexfer implements the wire encoding for secsh-filexfer as described in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 // Package sshfx implements the wire encoding for secsh-filexfer as described in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
package filexfer package sshfx
// PacketMarshaller narrowly defines packets that will only be transmitted. // PacketMarshaller narrowly defines packets that will only be transmitted.
// //

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"fmt" "fmt"
@ -10,7 +10,7 @@ type Status uint32
// Defines the various SSH_FX_* values. // Defines the various SSH_FX_* values.
const ( const (
// see draft-ietf-secsh-filexfer-02 // see draft-ietf-secsh-filexfer-02
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
StatusOK = Status(iota) StatusOK = Status(iota)
StatusEOF StatusEOF
StatusNoSuchFile StatusNoSuchFile
@ -21,28 +21,28 @@ const (
StatusConnectionLost StatusConnectionLost
StatusOPUnsupported StatusOPUnsupported
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-7 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-03.txt#section-7
StatusV4InvalidHandle StatusV4InvalidHandle
StatusV4NoSuchPath StatusV4NoSuchPath
StatusV4FileAlreadyExists StatusV4FileAlreadyExists
StatusV4WriteProtect StatusV4WriteProtect
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-7 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-04.txt#section-7
StatusV4NoMedia StatusV4NoMedia
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-7 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-05.txt#section-7
StatusV5NoSpaceOnFilesystem StatusV5NoSpaceOnFilesystem
StatusV5QuotaExceeded StatusV5QuotaExceeded
StatusV5UnknownPrincipal StatusV5UnknownPrincipal
StatusV5LockConflict StatusV5LockConflict
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-06#section-8 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-06.txt#section-8
StatusV6DirNotEmpty StatusV6DirNotEmpty
StatusV6NotADirectory StatusV6NotADirectory
StatusV6InvalidFilename StatusV6InvalidFilename
StatusV6LinkLoop StatusV6LinkLoop
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-8 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-8
StatusV6CannotDelete StatusV6CannotDelete
StatusV6InvalidParameter StatusV6InvalidParameter
StatusV6FileIsADirectory StatusV6FileIsADirectory
@ -50,10 +50,10 @@ const (
StatusV6ByteRangeLockRefused StatusV6ByteRangeLockRefused
StatusV6DeletePending StatusV6DeletePending
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-8.1 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-8.1
StatusV6FileCorrupt StatusV6FileCorrupt
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-10#section-9.1 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-10.txt#section-9.1
StatusV6OwnerInvalid StatusV6OwnerInvalid
StatusV6GroupInvalid StatusV6GroupInvalid

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bufio" "bufio"
@ -9,7 +9,7 @@ import (
"testing" "testing"
) )
// This string data is copied verbatim from https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13 // This string data is copied verbatim from https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-13.txt
var fxStandardsText = ` var fxStandardsText = `
SSH_FX_OK 0 SSH_FX_OK 0
SSH_FX_EOF 1 SSH_FX_EOF 1

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"fmt" "fmt"
@ -9,7 +9,7 @@ type PacketType uint8
// Request packet types. // Request packet types.
const ( const (
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
PacketTypeInit = PacketType(iota + 1) PacketTypeInit = PacketType(iota + 1)
PacketTypeVersion PacketTypeVersion
PacketTypeOpen PacketTypeOpen
@ -31,17 +31,17 @@ const (
PacketTypeReadLink PacketTypeReadLink
PacketTypeSymlink PacketTypeSymlink
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-07#section-3.3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-07.txt#section-3.3
PacketTypeV6Link PacketTypeV6Link
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-08#section-3.3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-08.txt#section-3.3
PacketTypeV6Block PacketTypeV6Block
PacketTypeV6Unblock PacketTypeV6Unblock
) )
// Response packet types. // Response packet types.
const ( const (
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
PacketTypeStatus = PacketType(iota + 101) PacketTypeStatus = PacketType(iota + 101)
PacketTypeHandle PacketTypeHandle
PacketTypeData PacketTypeData
@ -51,7 +51,7 @@ const (
// Extended packet types. // Extended packet types.
const ( const (
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
PacketTypeExtended = PacketType(iota + 200) PacketTypeExtended = PacketType(iota + 200)
PacketTypeExtendedReply PacketTypeExtendedReply
) )
@ -122,3 +122,48 @@ func (f PacketType) String() string {
return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f) return fmt.Sprintf("SSH_FXP_UNKNOWN(%d)", f)
} }
} }
func newPacketFromType(typ PacketType) (Packet, error) {
switch typ {
case PacketTypeOpen:
return new(OpenPacket), nil
case PacketTypeClose:
return new(ClosePacket), nil
case PacketTypeRead:
return new(ReadPacket), nil
case PacketTypeWrite:
return new(WritePacket), nil
case PacketTypeLStat:
return new(LStatPacket), nil
case PacketTypeFStat:
return new(FStatPacket), nil
case PacketTypeSetstat:
return new(SetstatPacket), nil
case PacketTypeFSetstat:
return new(FSetstatPacket), nil
case PacketTypeOpenDir:
return new(OpenDirPacket), nil
case PacketTypeReadDir:
return new(ReadDirPacket), nil
case PacketTypeRemove:
return new(RemovePacket), nil
case PacketTypeMkdir:
return new(MkdirPacket), nil
case PacketTypeRmdir:
return new(RmdirPacket), nil
case PacketTypeRealPath:
return new(RealPathPacket), nil
case PacketTypeStat:
return new(StatPacket), nil
case PacketTypeRename:
return new(RenamePacket), nil
case PacketTypeReadLink:
return new(ReadLinkPacket), nil
case PacketTypeSymlink:
return new(SymlinkPacket), nil
case PacketTypeExtended:
return new(ExtendedPacket), nil
default:
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
}
}

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bufio" "bufio"
@ -8,7 +8,8 @@ import (
"testing" "testing"
) )
// This string data is copied verbatim from https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13 // This string data is copied verbatim from https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-13.txt
// except where commented that it was taken from a different source.
var fxpStandardsText = ` var fxpStandardsText = `
SSH_FXP_INIT 1 SSH_FXP_INIT 1
SSH_FXP_VERSION 2 SSH_FXP_VERSION 2

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// ClosePacket defines the SSH_FXP_CLOSE packet. // ClosePacket defines the SSH_FXP_CLOSE packet.
type ClosePacket struct { type ClosePacket struct {
@ -27,18 +27,18 @@ func (p *ClosePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *ClosePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = ClosePacket{
return err Handle: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// ReadPacket defines the SSH_FXP_READ packet. // ReadPacket defines the SSH_FXP_READ packet.
type ReadPacket struct { type ReadPacket struct {
Handle string Handle string
Offset uint64 Offset uint64
Len uint32 Length uint32
} }
// Type returns the SSH_FXP_xy value associated with this packet type. // Type returns the SSH_FXP_xy value associated with this packet type.
@ -58,7 +58,7 @@ func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
buf.StartPacket(PacketTypeRead, reqid) buf.StartPacket(PacketTypeRead, reqid)
buf.AppendString(p.Handle) buf.AppendString(p.Handle)
buf.AppendUint64(p.Offset) buf.AppendUint64(p.Offset)
buf.AppendUint32(p.Len) buf.AppendUint32(p.Length)
return buf.Packet(payload) return buf.Packet(payload)
} }
@ -66,19 +66,13 @@ func (p *ReadPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *ReadPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = ReadPacket{
return err Handle: buf.ConsumeString(),
Offset: buf.ConsumeUint64(),
Length: buf.ConsumeUint32(),
} }
if p.Offset, err = buf.ConsumeUint64(); err != nil { return buf.Err
return err
}
if p.Len, err = buf.ConsumeUint32(); err != nil {
return err
}
return nil
} }
// WritePacket defines the SSH_FXP_WRITE packet. // WritePacket defines the SSH_FXP_WRITE packet.
@ -121,26 +115,13 @@ func (p *WritePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// //
// This means this _does not_ alias any of the data buffer that is passed in. // This means this _does not_ alias any of the data buffer that is passed in.
func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *WritePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = WritePacket{
return err Handle: buf.ConsumeString(),
Offset: buf.ConsumeUint64(),
Data: buf.ConsumeByteSliceCopy(p.Data),
} }
if p.Offset, err = buf.ConsumeUint64(); err != nil { return buf.Err
return err
}
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
} }
// FStatPacket defines the SSH_FXP_FSTAT packet. // FStatPacket defines the SSH_FXP_FSTAT packet.
@ -170,11 +151,11 @@ func (p *FStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *FStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = FStatPacket{
return err Handle: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// FSetstatPacket defines the SSH_FXP_FSETSTAT packet. // FSetstatPacket defines the SSH_FXP_FSETSTAT packet.
@ -207,8 +188,8 @@ func (p *FSetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *FSetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = FSetstatPacket{
return err Handle: buf.ConsumeString(),
} }
return p.Attrs.UnmarshalFrom(buf) return p.Attrs.UnmarshalFrom(buf)
@ -241,9 +222,9 @@ func (p *ReadDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *ReadDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = ReadDirPacket{
return err Handle: buf.ConsumeString(),
} }
return nil return buf.Err
} }

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"
@ -58,7 +58,7 @@ func TestReadPacket(t *testing.T) {
p := &ReadPacket{ p := &ReadPacket{
Handle: "somehandle", Handle: "somehandle",
Offset: offset, Offset: offset,
Len: length, Length: length,
} }
buf, err := ComposePacket(p.MarshalPacket(id, nil)) buf, err := ComposePacket(p.MarshalPacket(id, nil))
@ -94,8 +94,8 @@ func TestReadPacket(t *testing.T) {
t.Errorf("UnmarshalPacketBody(): Offset was %x, but expected %x", p.Offset, offset) t.Errorf("UnmarshalPacketBody(): Offset was %x, but expected %x", p.Offset, offset)
} }
if p.Len != length { if p.Length != length {
t.Errorf("UnmarshalPacketBody(): Len was %x, but expected %x", p.Len, length) t.Errorf("UnmarshalPacketBody(): Length was %x, but expected %x", p.Length, length)
} }
} }

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// InitPacket defines the SSH_FXP_INIT packet. // InitPacket defines the SSH_FXP_INIT packet.
type InitPacket struct { type InitPacket struct {
@ -33,8 +33,8 @@ func (p *InitPacket) MarshalBinary() ([]byte, error) {
func (p *InitPacket) UnmarshalBinary(data []byte) (err error) { func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
buf := NewBuffer(data) buf := NewBuffer(data)
if p.Version, err = buf.ConsumeUint32(); err != nil { *p = InitPacket{
return err Version: buf.ConsumeUint32(),
} }
for buf.Len() > 0 { for buf.Len() > 0 {
@ -46,7 +46,7 @@ func (p *InitPacket) UnmarshalBinary(data []byte) (err error) {
p.Extensions = append(p.Extensions, &ext) p.Extensions = append(p.Extensions, &ext)
} }
return nil return buf.Err
} }
// VersionPacket defines the SSH_FXP_VERSION packet. // VersionPacket defines the SSH_FXP_VERSION packet.
@ -82,8 +82,8 @@ func (p *VersionPacket) MarshalBinary() ([]byte, error) {
func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) { func (p *VersionPacket) UnmarshalBinary(data []byte) (err error) {
buf := NewBuffer(data) buf := NewBuffer(data)
if p.Version, err = buf.ConsumeUint32(); err != nil { *p = VersionPacket{
return err Version: buf.ConsumeUint32(),
} }
for buf.Len() > 0 { for buf.Len() > 0 {

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// SSH_FXF_* flags. // SSH_FXF_* flags.
const ( const (
@ -43,12 +43,9 @@ func (p *OpenPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *OpenPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Filename, err = buf.ConsumeString(); err != nil { *p = OpenPacket{
return err Filename: buf.ConsumeString(),
} PFlags: buf.ConsumeUint32(),
if p.PFlags, err = buf.ConsumeUint32(); err != nil {
return err
} }
return p.Attrs.UnmarshalFrom(buf) return p.Attrs.UnmarshalFrom(buf)
@ -81,9 +78,9 @@ func (p *OpenDirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *OpenDirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = OpenDirPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -60,11 +60,11 @@ func (ep *FSyncExtendedPacket) MarshalBinary() ([]byte, error) {
// UnmarshalFrom decodes the fsync@openssh.com extended packet-specific data from buf. // UnmarshalFrom decodes the fsync@openssh.com extended packet-specific data from buf.
func (ep *FSyncExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *FSyncExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.Handle, err = buf.ConsumeString(); err != nil { *ep = FSyncExtendedPacket{
return err Handle: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// UnmarshalBinary decodes the fsync@openssh.com extended packet-specific data into ep. // UnmarshalBinary decodes the fsync@openssh.com extended packet-specific data into ep.

View File

@ -62,15 +62,12 @@ func (ep *HardlinkExtendedPacket) MarshalBinary() ([]byte, error) {
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf. // UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
func (ep *HardlinkExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *HardlinkExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.OldPath, err = buf.ConsumeString(); err != nil { *ep = HardlinkExtendedPacket{
return err OldPath: buf.ConsumeString(),
NewPath: buf.ConsumeString(),
} }
if ep.NewPath, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep. // UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.

View File

@ -4,38 +4,38 @@ import (
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer" sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
) )
const extensionPosixRename = "posix-rename@openssh.com" const extensionPOSIXRename = "posix-rename@openssh.com"
// RegisterExtensionPosixRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package. // RegisterExtensionPOSIXRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package.
func RegisterExtensionPosixRename() { func RegisterExtensionPOSIXRename() {
sshfx.RegisterExtendedPacketType(extensionPosixRename, func() sshfx.ExtendedData { sshfx.RegisterExtendedPacketType(extensionPOSIXRename, func() sshfx.ExtendedData {
return new(PosixRenameExtendedPacket) return new(POSIXRenameExtendedPacket)
}) })
} }
// ExtensionPosixRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket. // ExtensionPOSIXRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
func ExtensionPosixRename() *sshfx.ExtensionPair { func ExtensionPOSIXRename() *sshfx.ExtensionPair {
return &sshfx.ExtensionPair{ return &sshfx.ExtensionPair{
Name: extensionPosixRename, Name: extensionPOSIXRename,
Data: "1", Data: "1",
} }
} }
// PosixRenameExtendedPacket defines the posix-rename@openssh.com extend packet. // POSIXRenameExtendedPacket defines the posix-rename@openssh.com extend packet.
type PosixRenameExtendedPacket struct { type POSIXRenameExtendedPacket struct {
OldPath string OldPath string
NewPath string NewPath string
} }
// Type returns the SSH_FXP_EXTENDED packet type. // Type returns the SSH_FXP_EXTENDED packet type.
func (ep *PosixRenameExtendedPacket) Type() sshfx.PacketType { func (ep *POSIXRenameExtendedPacket) Type() sshfx.PacketType {
return sshfx.PacketTypeExtended return sshfx.PacketTypeExtended
} }
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet. // MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
func (ep *PosixRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { func (ep *POSIXRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
p := &sshfx.ExtendedPacket{ p := &sshfx.ExtendedPacket{
ExtendedRequest: extensionPosixRename, ExtendedRequest: extensionPOSIXRename,
Data: ep, Data: ep,
} }
@ -43,7 +43,7 @@ func (ep *PosixRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (head
} }
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data. // MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
func (ep *PosixRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) { func (ep *POSIXRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
buf.AppendString(ep.OldPath) buf.AppendString(ep.OldPath)
buf.AppendString(ep.NewPath) buf.AppendString(ep.NewPath)
} }
@ -51,7 +51,7 @@ func (ep *PosixRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data. // MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
// //
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet. // NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
func (ep *PosixRenameExtendedPacket) MarshalBinary() ([]byte, error) { func (ep *POSIXRenameExtendedPacket) MarshalBinary() ([]byte, error) {
// string(oldpath) + string(newpath) // string(oldpath) + string(newpath)
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath) size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
@ -61,19 +61,16 @@ func (ep *PosixRenameExtendedPacket) MarshalBinary() ([]byte, error) {
} }
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf. // UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
func (ep *PosixRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *POSIXRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.OldPath, err = buf.ConsumeString(); err != nil { *ep = POSIXRenameExtendedPacket{
return err OldPath: buf.ConsumeString(),
NewPath: buf.ConsumeString(),
} }
if ep.NewPath, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep. // UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
func (ep *PosixRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) { func (ep *POSIXRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) {
return ep.UnmarshalFrom(sshfx.NewBuffer(data)) return ep.UnmarshalFrom(sshfx.NewBuffer(data))
} }

View File

@ -7,20 +7,20 @@ import (
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer" sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
) )
var _ sshfx.PacketMarshaller = &PosixRenameExtendedPacket{} var _ sshfx.PacketMarshaller = &POSIXRenameExtendedPacket{}
func init() { func init() {
RegisterExtensionPosixRename() RegisterExtensionPOSIXRename()
} }
func TestPosixRenameExtendedPacket(t *testing.T) { func TestPOSIXRenameExtendedPacket(t *testing.T) {
const ( const (
id = 42 id = 42
oldpath = "/foo" oldpath = "/foo"
newpath = "/bar" newpath = "/bar"
) )
ep := &PosixRenameExtendedPacket{ ep := &POSIXRenameExtendedPacket{
OldPath: oldpath, OldPath: oldpath,
NewPath: newpath, NewPath: newpath,
} }
@ -50,13 +50,13 @@ func TestPosixRenameExtendedPacket(t *testing.T) {
t.Fatal("unexpected error:", err) t.Fatal("unexpected error:", err)
} }
if p.ExtendedRequest != extensionPosixRename { if p.ExtendedRequest != extensionPOSIXRename {
t.Errorf("UnmarshalPacketBody(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extensionPosixRename) t.Errorf("UnmarshalPacketBody(): ExtendedRequest was %q, but expected %q", p.ExtendedRequest, extensionPOSIXRename)
} }
ep, ok := p.Data.(*PosixRenameExtendedPacket) ep, ok := p.Data.(*POSIXRenameExtendedPacket)
if !ok { if !ok {
t.Fatalf("UnmarshaledPacketBody(): Data was type %T, but expected *PosixRenameExtendedPacket", p.Data) t.Fatalf("UnmarshaledPacketBody(): Data was type %T, but expected *POSIXRenameExtendedPacket", p.Data)
} }
if ep.OldPath != oldpath { if ep.OldPath != oldpath {

View File

@ -61,11 +61,11 @@ func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep. // UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.Path, err = buf.ConsumeString(); err != nil { *ep = StatVFSExtendedPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep. // UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
@ -130,11 +130,11 @@ func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep. // UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.Path, err = buf.ConsumeString(); err != nil { *ep = FStatVFSExtendedPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep. // UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
@ -213,41 +213,21 @@ func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep. // UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) { func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
if ep.BlockSize, err = buf.ConsumeUint64(); err != nil { *ep = StatVFSExtendedReplyPacket{
return err BlockSize: buf.ConsumeUint64(),
} FragmentSize: buf.ConsumeUint64(),
if ep.FragmentSize, err = buf.ConsumeUint64(); err != nil { Blocks: buf.ConsumeUint64(),
return err BlocksFree: buf.ConsumeUint64(),
} BlocksAvail: buf.ConsumeUint64(),
if ep.Blocks, err = buf.ConsumeUint64(); err != nil { Files: buf.ConsumeUint64(),
return err FilesFree: buf.ConsumeUint64(),
} FilesAvail: buf.ConsumeUint64(),
if ep.BlocksFree, err = buf.ConsumeUint64(); err != nil { FilesystemID: buf.ConsumeUint64(),
return err MountFlags: buf.ConsumeUint64(),
} MaxNameLength: buf.ConsumeUint64(),
if ep.BlocksAvail, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.Files, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.FilesFree, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.FilesAvail, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.FilesystemID, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.MountFlags, err = buf.ConsumeUint64(); err != nil {
return err
}
if ep.MaxNameLength, err = buf.ConsumeUint64(); err != nil {
return err
} }
return nil return buf.Err
} }
// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep. // UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.

View File

@ -1,59 +1,13 @@
package filexfer package sshfx
import ( import (
"errors" "errors"
"fmt"
"io" "io"
) )
// smallBufferSize is an initial allocation minimal capacity. // smallBufferSize is an initial allocation minimal capacity.
const smallBufferSize = 64 const smallBufferSize = 64
func newPacketFromType(typ PacketType) (Packet, error) {
switch typ {
case PacketTypeOpen:
return new(OpenPacket), nil
case PacketTypeClose:
return new(ClosePacket), nil
case PacketTypeRead:
return new(ReadPacket), nil
case PacketTypeWrite:
return new(WritePacket), nil
case PacketTypeLStat:
return new(LStatPacket), nil
case PacketTypeFStat:
return new(FStatPacket), nil
case PacketTypeSetstat:
return new(SetstatPacket), nil
case PacketTypeFSetstat:
return new(FSetstatPacket), nil
case PacketTypeOpenDir:
return new(OpenDirPacket), nil
case PacketTypeReadDir:
return new(ReadDirPacket), nil
case PacketTypeRemove:
return new(RemovePacket), nil
case PacketTypeMkdir:
return new(MkdirPacket), nil
case PacketTypeRmdir:
return new(RmdirPacket), nil
case PacketTypeRealPath:
return new(RealPathPacket), nil
case PacketTypeStat:
return new(StatPacket), nil
case PacketTypeRename:
return new(RenamePacket), nil
case PacketTypeReadLink:
return new(ReadLinkPacket), nil
case PacketTypeSymlink:
return new(SymlinkPacket), nil
case PacketTypeExtended:
return new(ExtendedPacket), nil
default:
return nil, fmt.Errorf("unexpected request packet type: %v", typ)
}
}
// RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02 // RawPacket implements the general packet format from draft-ietf-secsh-filexfer-02
// //
// RawPacket is intended for use in clients receiving responses, // RawPacket is intended for use in clients receiving responses,
@ -63,7 +17,7 @@ func newPacketFromType(typ PacketType) (Packet, error) {
// For servers expecting to receive arbitrary request packet types, // For servers expecting to receive arbitrary request packet types,
// use RequestPacket. // use RequestPacket.
// //
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3 // Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
type RawPacket struct { type RawPacket struct {
PacketType PacketType PacketType PacketType
RequestID uint32 RequestID uint32
@ -110,19 +64,14 @@ func (p *RawPacket) MarshalBinary() ([]byte, error) {
// The Data field will alias the passed in Buffer, // The Data field will alias the passed in Buffer,
// so the buffer passed in should not be reused before RawPacket.Reset(). // so the buffer passed in should not be reused before RawPacket.Reset().
func (p *RawPacket) UnmarshalFrom(buf *Buffer) error { func (p *RawPacket) UnmarshalFrom(buf *Buffer) error {
typ, err := buf.ConsumeUint8() *p = RawPacket{
if err != nil { PacketType: PacketType(buf.ConsumeUint8()),
return err RequestID: buf.ConsumeUint32(),
}
p.PacketType = PacketType(typ)
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
return err
} }
p.Data = *buf p.Data = *buf
return nil
return buf.Err
} }
// UnmarshalBinary decodes a full raw packet out of the given data. // UnmarshalBinary decodes a full raw packet out of the given data.
@ -225,7 +174,7 @@ func (p *RawPacket) ReadFrom(r io.Reader, b []byte, maxPacketLength uint32) erro
// where automatic unmarshaling of the packet body does not make sense, // where automatic unmarshaling of the packet body does not make sense,
// use RawPacket. // use RawPacket.
// //
// Defined in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-3 // Defined in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-3
type RequestPacket struct { type RequestPacket struct {
RequestID uint32 RequestID uint32
@ -268,18 +217,19 @@ func (p *RequestPacket) MarshalBinary() ([]byte, error) {
// The Request field may alias the passed in Buffer, (e.g. SSH_FXP_WRITE), // 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(). // so the buffer passed in should not be reused before RequestPacket.Reset().
func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error { func (p *RequestPacket) UnmarshalFrom(buf *Buffer) error {
typ, err := buf.ConsumeUint8() typ := PacketType(buf.ConsumeUint8())
if buf.Err != nil {
return buf.Err
}
req, err := newPacketFromType(typ)
if err != nil { if err != nil {
return err return err
} }
p.Request, err = newPacketFromType(PacketType(typ)) *p = RequestPacket{
if err != nil { RequestID: buf.ConsumeUint32(),
return err Request: req,
}
if p.RequestID, err = buf.ConsumeUint32(); err != nil {
return err
} }
return p.Request.UnmarshalPacketBody(buf) return p.Request.UnmarshalPacketBody(buf)

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// LStatPacket defines the SSH_FXP_LSTAT packet. // LStatPacket defines the SSH_FXP_LSTAT packet.
type LStatPacket struct { type LStatPacket struct {
@ -27,11 +27,11 @@ func (p *LStatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *LStatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = LStatPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// SetstatPacket defines the SSH_FXP_SETSTAT packet. // SetstatPacket defines the SSH_FXP_SETSTAT packet.
@ -64,8 +64,8 @@ func (p *SetstatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *SetstatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = SetstatPacket{
return err Path: buf.ConsumeString(),
} }
return p.Attrs.UnmarshalFrom(buf) return p.Attrs.UnmarshalFrom(buf)
@ -98,11 +98,11 @@ func (p *RemovePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *RemovePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = RemovePacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// MkdirPacket defines the SSH_FXP_MKDIR packet. // MkdirPacket defines the SSH_FXP_MKDIR packet.
@ -135,8 +135,8 @@ func (p *MkdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *MkdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = MkdirPacket{
return err Path: buf.ConsumeString(),
} }
return p.Attrs.UnmarshalFrom(buf) return p.Attrs.UnmarshalFrom(buf)
@ -169,11 +169,11 @@ func (p *RmdirPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []b
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *RmdirPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = RmdirPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// RealPathPacket defines the SSH_FXP_REALPATH packet. // RealPathPacket defines the SSH_FXP_REALPATH packet.
@ -203,11 +203,11 @@ func (p *RealPathPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *RealPathPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = RealPathPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// StatPacket defines the SSH_FXP_STAT packet. // StatPacket defines the SSH_FXP_STAT packet.
@ -237,11 +237,11 @@ func (p *StatPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *StatPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = StatPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// RenamePacket defines the SSH_FXP_RENAME packet. // RenamePacket defines the SSH_FXP_RENAME packet.
@ -274,15 +274,12 @@ func (p *RenamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *RenamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.OldPath, err = buf.ConsumeString(); err != nil { *p = RenamePacket{
return err OldPath: buf.ConsumeString(),
NewPath: buf.ConsumeString(),
} }
if p.NewPath, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// ReadLinkPacket defines the SSH_FXP_READLINK packet. // ReadLinkPacket defines the SSH_FXP_READLINK packet.
@ -312,11 +309,11 @@ func (p *ReadLinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *ReadLinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Path, err = buf.ConsumeString(); err != nil { *p = ReadLinkPacket{
return err Path: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// SymlinkPacket defines the SSH_FXP_SYMLINK packet. // SymlinkPacket defines the SSH_FXP_SYMLINK packet.
@ -355,14 +352,11 @@ func (p *SymlinkPacket) MarshalPacket(reqid uint32, b []byte) (header, payload [
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *SymlinkPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
// Arguments were inadvertently reversed. *p = SymlinkPacket{
if p.TargetPath, err = buf.ConsumeString(); err != nil { // Arguments were inadvertently reversed.
return err TargetPath: buf.ConsumeString(),
LinkPath: buf.ConsumeString(),
} }
if p.LinkPath, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
// FileMode represents a files mode and permission bits. // FileMode represents a files mode and permission bits.
// The bits are defined according to POSIX standards, // The bits are defined according to POSIX standards,

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"fmt" "fmt"
@ -6,7 +6,7 @@ import (
// StatusPacket defines the SSH_FXP_STATUS packet. // StatusPacket defines the SSH_FXP_STATUS packet.
// //
// Specified in https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 // Specified in https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-7
type StatusPacket struct { type StatusPacket struct {
StatusCode Status StatusCode Status
ErrorMessage string ErrorMessage string
@ -19,7 +19,7 @@ func (p *StatusPacket) Error() string {
return "sftp: " + p.StatusCode.String() return "sftp: " + p.StatusCode.String()
} }
return fmt.Sprintf("sftp: %q (%s)", p.ErrorMessage, p.StatusCode) return fmt.Sprintf("sftp: %s: %q", p.StatusCode, p.ErrorMessage)
} }
// Is returns true if target is a StatusPacket with the same StatusCode, // Is returns true if target is a StatusPacket with the same StatusCode,
@ -57,21 +57,13 @@ func (p *StatusPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *StatusPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
statusCode, err := buf.ConsumeUint32() *p = StatusPacket{
if err != nil { StatusCode: Status(buf.ConsumeUint32()),
return err ErrorMessage: buf.ConsumeString(),
} LanguageTag: buf.ConsumeString(),
p.StatusCode = Status(statusCode)
if p.ErrorMessage, err = buf.ConsumeString(); err != nil {
return err
} }
if p.LanguageTag, err = buf.ConsumeString(); err != nil { return buf.Err
return err
}
return nil
} }
// HandlePacket defines the SSH_FXP_HANDLE packet. // HandlePacket defines the SSH_FXP_HANDLE packet.
@ -101,11 +93,11 @@ func (p *HandlePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *HandlePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
if p.Handle, err = buf.ConsumeString(); err != nil { *p = HandlePacket{
return err Handle: buf.ConsumeString(),
} }
return nil return buf.Err
} }
// DataPacket defines the SSH_FXP_DATA packet. // DataPacket defines the SSH_FXP_DATA packet.
@ -143,18 +135,11 @@ func (p *DataPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
// //
// This means this _does not_ alias any of the data buffer that is passed in. // This means this _does not_ alias any of the data buffer that is passed in.
func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *DataPacket) UnmarshalPacketBody(buf *Buffer) (err error) {
data, err := buf.ConsumeByteSlice() *p = DataPacket{
if err != nil { Data: buf.ConsumeByteSliceCopy(p.Data),
return err
} }
if len(p.Data) < len(data) { return buf.Err
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. // NamePacket defines the SSH_FXP_NAME packet.
@ -193,14 +178,16 @@ func (p *NamePacket) MarshalPacket(reqid uint32, b []byte) (header, payload []by
// UnmarshalPacketBody unmarshals the packet body from the given Buffer. // UnmarshalPacketBody unmarshals the packet body from the given Buffer.
// It is assumed that the uint32(request-id) has already been consumed. // It is assumed that the uint32(request-id) has already been consumed.
func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) { func (p *NamePacket) UnmarshalPacketBody(buf *Buffer) (err error) {
count, err := buf.ConsumeUint32() count := buf.ConsumeCount()
if err != nil { if buf.Err != nil {
return err return buf.Err
} }
p.Entries = make([]*NameEntry, 0, count) *p = NamePacket{
Entries: make([]*NameEntry, 0, count),
}
for i := uint32(0); i < count; i++ { for i := 0; i < count; i++ {
var e NameEntry var e NameEntry
if err := e.UnmarshalFrom(buf); err != nil { if err := e.UnmarshalFrom(buf); err != nil {
return err return err

View File

@ -1,4 +1,4 @@
package filexfer package sshfx
import ( import (
"bytes" "bytes"

View File

@ -42,41 +42,41 @@ func TestRunLsWithLicensesFileWithOSLookup(t *testing.T) {
} }
/* /*
The format of the `longname' field is unspecified by this protocol. The format of the `longname' field is unspecified by this protocol.
It MUST be suitable for use in the output of a directory listing It MUST be suitable for use in the output of a directory listing
command (in fact, the recommended operation for a directory listing command (in fact, the recommended operation for a directory listing
command is to simply display this data). However, clients SHOULD NOT command is to simply display this data). However, clients SHOULD NOT
attempt to parse the longname field for file attributes; they SHOULD attempt to parse the longname field for file attributes; they SHOULD
use the attrs field instead. use the attrs field instead.
The recommended format for the longname field is as follows: The recommended format for the longname field is as follows:
-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
1234567890 123 12345678 12345678 12345678 123456789012 1234567890 123 12345678 12345678 12345678 123456789012
Here, the first line is sample output, and the second field indicates Here, the first line is sample output, and the second field indicates
widths of the various fields. Fields are separated by spaces. The widths of the various fields. Fields are separated by spaces. The
first field lists file permissions for user, group, and others; the first field lists file permissions for user, group, and others; the
second field is link count; the third field is the name of the user second field is link count; the third field is the name of the user
who owns the file; the fourth field is the name of the group that who owns the file; the fourth field is the name of the group that
owns the file; the fifth field is the size of the file in bytes; the owns the file; the fifth field is the size of the file in bytes; the
sixth field (which actually may contain spaces, but is fixed to 12 sixth field (which actually may contain spaces, but is fixed to 12
characters) is the file modification time, and the seventh field is characters) is the file modification time, and the seventh field is
the file name. Each field is specified to be a minimum of certain the file name. Each field is specified to be a minimum of certain
number of character positions (indicated by the second line above), number of character positions (indicated by the second line above),
but may also be longer if the data does not fit in the specified but may also be longer if the data does not fit in the specified
length. length.
The SSH_FXP_ATTRS response has the following format: The SSH_FXP_ATTRS response has the following format:
uint32 id uint32 id
ATTRS attrs ATTRS attrs
where `id' is the request identifier, and `attrs' is the returned where `id' is the request identifier, and `attrs' is the returned
file attributes as described in Section ``File Attributes''. file attributes as described in Section File Attributes.
N.B.: FileZilla does parse this ls formatting, and so not rendering it N.B.: FileZilla does parse this ls formatting, and so not rendering it
on any particular GOOS/GOARCH can cause compatibility issues with this client. on any particular GOOS/GOARCH can cause compatibility issues with this client.
*/ */
func runLsTestHelper(t *testing.T, result, expectedType, path string) { func runLsTestHelper(t *testing.T, result, expectedType, path string) {
// using regular expressions to make tests work on all systems // using regular expressions to make tests work on all systems

View File

@ -1,3 +1,4 @@
//go:build plan9
// +build plan9 // +build plan9
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build windows || android
// +build windows android // +build windows android
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || js
// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js // +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js
package sftp package sftp

View File

@ -40,7 +40,7 @@ func newPktMgr(sender packetSender) *packetManager {
return s return s
} }
//// packet ordering // // packet ordering
func (s *packetManager) newOrderID() uint32 { func (s *packetManager) newOrderID() uint32 {
s.packetCount++ s.packetCount++
return s.packetCount return s.packetCount
@ -89,7 +89,7 @@ func (o orderedPackets) Sort() {
}) })
} }
//// packet registry // // packet registry
// register incoming packets to be handled // register incoming packets to be handled
func (s *packetManager) incomingPacket(pkt orderedRequest) { func (s *packetManager) incomingPacket(pkt orderedRequest) {
s.working.Add(1) s.working.Add(1)

View File

@ -31,7 +31,7 @@ type notReadOnly interface {
notReadOnly() notReadOnly()
} }
//// define types by adding methods // // define types by adding methods
// hasPath // hasPath
func (p *sshFxpLstatPacket) getPath() string { return p.Path } func (p *sshFxpLstatPacket) getPath() string { return p.Path }
func (p *sshFxpStatPacket) getPath() string { return p.Path } func (p *sshFxpStatPacket) getPath() string { return p.Path }

View File

@ -1,3 +1,4 @@
//go:build !debug
// +build !debug // +build !debug
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build plan9
// +build plan9 // +build plan9
package sftp package sftp

View File

@ -28,7 +28,7 @@ then sends to the client.
Handler for "Put" method and returns an io.Writer for the file which the server Handler for "Put" method and returns an io.Writer for the file which the server
then writes the uploaded file to. The file opening "pflags" are currently then writes the uploaded file to. The file opening "pflags" are currently
preserved in the Request.Flags field as a 32bit bitmask value. See the [SFTP preserved in the Request.Flags field as a 32bit bitmask value. See the [SFTP
spec](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.3) for spec](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt#section-6.3) for
details. details.
### Filecmd(*Request) error ### Filecmd(*Request) error

View File

@ -1,3 +1,4 @@
//go:build !windows && !plan9
// +build !windows,!plan9 // +build !windows,!plan9
package sftp package sftp

View File

@ -24,7 +24,7 @@ const (
// Server is an SSH File Transfer Protocol (sftp) server. // Server is an SSH File Transfer Protocol (sftp) server.
// This is intended to provide the sftp subsystem to an ssh server daemon. // This is intended to provide the sftp subsystem to an ssh server daemon.
// This implementation currently supports most of sftp server protocol version 3, // This implementation currently supports most of sftp server protocol version 3,
// as specified at http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 // as specified at https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
type Server struct { type Server struct {
*serverConn *serverConn
debugStream io.Writer debugStream io.Writer

View File

@ -1,3 +1,4 @@
//go:build darwin || linux
// +build darwin linux // +build darwin linux
// fill in statvfs structure with OS specific values // fill in statvfs structure with OS specific values

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package sftp package sftp

View File

@ -1,3 +1,4 @@
//go:build !darwin && !linux && !plan9
// +build !darwin,!linux,!plan9 // +build !darwin,!linux,!plan9
package sftp package sftp

View File

@ -1,5 +1,5 @@
// Package sftp implements the SSH File Transfer Protocol as described in // Package sftp implements the SSH File Transfer Protocol as described in
// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
package sftp package sftp
import ( import (

View File

@ -1,3 +1,4 @@
//go:build plan9 || windows || (js && wasm)
// +build plan9 windows js,wasm // +build plan9 windows js,wasm
// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of // Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of

View File

@ -1,4 +1,6 @@
// +build !plan9,!windows //go:build !plan9 && !windows && (!js || !wasm)
// +build !plan9
// +build !windows
// +build !js !wasm // +build !js !wasm
package sftp package sftp