mirror of https://github.com/pkg/sftp.git
refactor sshfx encoding, fix link rot, go fmt
This commit is contained in:
parent
971c283182
commit
bd61319b07
2
attrs.go
2
attrs.go
|
@ -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"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build plan9 || windows || android
|
||||||
// +build plan9 windows android
|
// +build plan9 windows android
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
debug.go
1
debug.go
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build debug
|
||||||
// +build debug
|
// +build debug
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
1
fuzz.go
1
fuzz.go
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build gofuzz
|
||||||
// +build gofuzz
|
// +build gofuzz
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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 two’s complement.
|
// 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.
|
// 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 two’s complement.
|
// AppendInt64 appends a single int64 into the buffer, in network byte order (big-endian) with two’s 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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
// FileMode represents a file’s mode and permission bits.
|
// FileMode represents a file’s mode and permission bits.
|
||||||
// The bits are defined according to POSIX standards,
|
// The bits are defined according to POSIX standards,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package filexfer
|
package sshfx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build plan9
|
||||||
// +build plan9
|
// +build plan9
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows || android
|
||||||
// +build windows android
|
// +build windows android
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !debug
|
||||||
// +build !debug
|
// +build !debug
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build plan9
|
||||||
// +build plan9
|
// +build plan9
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !windows && !plan9
|
||||||
// +build !windows,!plan9
|
// +build !windows,!plan9
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !darwin && !linux && !plan9
|
||||||
// +build !darwin,!linux,!plan9
|
// +build !darwin,!linux,!plan9
|
||||||
|
|
||||||
package sftp
|
package sftp
|
||||||
|
|
2
sftp.go
2
sftp.go
|
@ -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 (
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue