MarshalBinary now gives a 4-byte header for length, marshalPacket gives a two-stage write

This commit is contained in:
Cassondra Foesch 2020-10-28 20:54:31 +00:00
parent afc8d7b11a
commit 5e8f9f4960
4 changed files with 217 additions and 102 deletions

View File

@ -31,7 +31,7 @@ func fake(rid, order uint32) fakepacket {
} }
func (fakepacket) MarshalBinary() ([]byte, error) { func (fakepacket) MarshalBinary() ([]byte, error) {
return []byte{}, nil return make([]byte, 4), nil
} }
func (fakepacket) UnmarshalBinary([]byte) error { func (fakepacket) UnmarshalBinary([]byte) error {

289
packet.go
View File

@ -116,26 +116,45 @@ func unmarshalStringSafe(b []byte) (string, []byte, error) {
return string(b[:n]), b[n:], nil return string(b[:n]), b[n:], nil
} }
type packetMarshaler interface {
marshalPacket() (header, payload []byte, err error)
}
func marshalPacket(m encoding.BinaryMarshaler) (header, payload []byte, err error) {
if m, ok := m.(packetMarshaler); ok {
return m.marshalPacket()
}
header, err = m.MarshalBinary()
return
}
// sendPacket marshals p according to RFC 4234. // sendPacket marshals p according to RFC 4234.
func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error { func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error {
bb, err := m.MarshalBinary() header, payload, err := marshalPacket(m)
if err != nil { if err != nil {
return errors.Errorf("binary marshaller failed: %v", err) return errors.Errorf("binary marshaller failed: %v", err)
} }
if debugDumpTxPacketBytes {
debug("send packet: %s %d bytes %x", fxp(bb[0]), len(bb), bb[1:])
} else if debugDumpTxPacket {
debug("send packet: %s %d bytes", fxp(bb[0]), len(bb))
}
// Slide packet down 4 bytes to make room for length header.
packet := append(bb, make([]byte, 4)...) // optimistically assume bb has capacity
copy(packet[4:], bb)
binary.BigEndian.PutUint32(packet[:4], uint32(len(bb)))
_, err = w.Write(packet) length := len(header) + len(payload) - 4 // subtract the uint32(length) from the start
if err != nil { if debugDumpTxPacketBytes {
debug("send packet: %s %d bytes %x%x", fxp(header[4]), length, header[5:], payload)
} else if debugDumpTxPacket {
debug("send packet: %s %d bytes", fxp(header[4]), length)
}
binary.BigEndian.PutUint32(header[:4], uint32(length))
if _, err := w.Write(header); err != nil {
return errors.Errorf("failed to send packet: %v", err) return errors.Errorf("failed to send packet: %v", err)
} }
if len(payload) > 0 {
if _, err := w.Write(payload); err != nil {
return errors.Errorf("failed to send packet payload: %v", err)
}
}
return nil return nil
} }
@ -200,18 +219,20 @@ type sshFxInitPacket struct {
} }
func (p sshFxInitPacket) MarshalBinary() ([]byte, error) { func (p sshFxInitPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 // byte + uint32 l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(version)
for _, e := range p.Extensions { for _, e := range p.Extensions {
l += 4 + len(e.Name) + 4 + len(e.Data) l += 4 + len(e.Name) + 4 + len(e.Data)
} }
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpInit) b = append(b, sshFxpInit)
b = marshalUint32(b, p.Version) b = marshalUint32(b, p.Version)
for _, e := range p.Extensions { for _, e := range p.Extensions {
b = marshalString(b, e.Name) b = marshalString(b, e.Name)
b = marshalString(b, e.Data) b = marshalString(b, e.Data)
} }
return b, nil return b, nil
} }
@ -241,29 +262,32 @@ type sshExtensionPair struct {
} }
func (p sshFxVersionPacket) MarshalBinary() ([]byte, error) { func (p sshFxVersionPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 // byte + uint32 l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(version)
for _, e := range p.Extensions { for _, e := range p.Extensions {
l += 4 + len(e.Name) + 4 + len(e.Data) l += 4 + len(e.Name) + 4 + len(e.Data)
} }
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpVersion) b = append(b, sshFxpVersion)
b = marshalUint32(b, p.Version) b = marshalUint32(b, p.Version)
for _, e := range p.Extensions { for _, e := range p.Extensions {
b = marshalString(b, e.Name) b = marshalString(b, e.Name)
b = marshalString(b, e.Data) b = marshalString(b, e.Data)
} }
return b, nil return b, nil
} }
func marshalIDString(packetType byte, id uint32, str string) ([]byte, error) { func marshalIDStringPacket(packetType byte, id uint32, str string) ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(str) 4 + len(str)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, packetType) b = append(b, packetType)
b = marshalUint32(b, id) b = marshalUint32(b, id)
b = marshalString(b, str) b = marshalString(b, str)
return b, nil return b, nil
} }
@ -285,7 +309,7 @@ type sshFxpReaddirPacket struct {
func (p sshFxpReaddirPacket) id() uint32 { return p.ID } func (p sshFxpReaddirPacket) id() uint32 { return p.ID }
func (p sshFxpReaddirPacket) MarshalBinary() ([]byte, error) { func (p sshFxpReaddirPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpReaddir, p.ID, p.Handle) return marshalIDStringPacket(sshFxpReaddir, p.ID, p.Handle)
} }
func (p *sshFxpReaddirPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpReaddirPacket) UnmarshalBinary(b []byte) error {
@ -300,7 +324,7 @@ type sshFxpOpendirPacket struct {
func (p sshFxpOpendirPacket) id() uint32 { return p.ID } func (p sshFxpOpendirPacket) id() uint32 { return p.ID }
func (p sshFxpOpendirPacket) MarshalBinary() ([]byte, error) { func (p sshFxpOpendirPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpOpendir, p.ID, p.Path) return marshalIDStringPacket(sshFxpOpendir, p.ID, p.Path)
} }
func (p *sshFxpOpendirPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpOpendirPacket) UnmarshalBinary(b []byte) error {
@ -315,7 +339,7 @@ type sshFxpLstatPacket struct {
func (p sshFxpLstatPacket) id() uint32 { return p.ID } func (p sshFxpLstatPacket) id() uint32 { return p.ID }
func (p sshFxpLstatPacket) MarshalBinary() ([]byte, error) { func (p sshFxpLstatPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpLstat, p.ID, p.Path) return marshalIDStringPacket(sshFxpLstat, p.ID, p.Path)
} }
func (p *sshFxpLstatPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpLstatPacket) UnmarshalBinary(b []byte) error {
@ -330,7 +354,7 @@ type sshFxpStatPacket struct {
func (p sshFxpStatPacket) id() uint32 { return p.ID } func (p sshFxpStatPacket) id() uint32 { return p.ID }
func (p sshFxpStatPacket) MarshalBinary() ([]byte, error) { func (p sshFxpStatPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpStat, p.ID, p.Path) return marshalIDStringPacket(sshFxpStat, p.ID, p.Path)
} }
func (p *sshFxpStatPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpStatPacket) UnmarshalBinary(b []byte) error {
@ -345,7 +369,7 @@ type sshFxpFstatPacket struct {
func (p sshFxpFstatPacket) id() uint32 { return p.ID } func (p sshFxpFstatPacket) id() uint32 { return p.ID }
func (p sshFxpFstatPacket) MarshalBinary() ([]byte, error) { func (p sshFxpFstatPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpFstat, p.ID, p.Handle) return marshalIDStringPacket(sshFxpFstat, p.ID, p.Handle)
} }
func (p *sshFxpFstatPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpFstatPacket) UnmarshalBinary(b []byte) error {
@ -360,7 +384,7 @@ type sshFxpClosePacket struct {
func (p sshFxpClosePacket) id() uint32 { return p.ID } func (p sshFxpClosePacket) id() uint32 { return p.ID }
func (p sshFxpClosePacket) MarshalBinary() ([]byte, error) { func (p sshFxpClosePacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpClose, p.ID, p.Handle) return marshalIDStringPacket(sshFxpClose, p.ID, p.Handle)
} }
func (p *sshFxpClosePacket) UnmarshalBinary(b []byte) error { func (p *sshFxpClosePacket) UnmarshalBinary(b []byte) error {
@ -375,7 +399,7 @@ type sshFxpRemovePacket struct {
func (p sshFxpRemovePacket) id() uint32 { return p.ID } func (p sshFxpRemovePacket) id() uint32 { return p.ID }
func (p sshFxpRemovePacket) MarshalBinary() ([]byte, error) { func (p sshFxpRemovePacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpRemove, p.ID, p.Filename) return marshalIDStringPacket(sshFxpRemove, p.ID, p.Filename)
} }
func (p *sshFxpRemovePacket) UnmarshalBinary(b []byte) error { func (p *sshFxpRemovePacket) UnmarshalBinary(b []byte) error {
@ -390,7 +414,7 @@ type sshFxpRmdirPacket struct {
func (p sshFxpRmdirPacket) id() uint32 { return p.ID } func (p sshFxpRmdirPacket) id() uint32 { return p.ID }
func (p sshFxpRmdirPacket) MarshalBinary() ([]byte, error) { func (p sshFxpRmdirPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpRmdir, p.ID, p.Path) return marshalIDStringPacket(sshFxpRmdir, p.ID, p.Path)
} }
func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpRmdirPacket) UnmarshalBinary(b []byte) error {
@ -406,15 +430,16 @@ type sshFxpSymlinkPacket struct {
func (p sshFxpSymlinkPacket) id() uint32 { return p.ID } func (p sshFxpSymlinkPacket) id() uint32 { return p.ID }
func (p sshFxpSymlinkPacket) MarshalBinary() ([]byte, error) { func (p sshFxpSymlinkPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Targetpath) + 4 + len(p.Targetpath) +
4 + len(p.Linkpath) 4 + len(p.Linkpath)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpSymlink) b = append(b, sshFxpSymlink)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Targetpath) b = marshalString(b, p.Targetpath)
b = marshalString(b, p.Linkpath) b = marshalString(b, p.Linkpath)
return b, nil return b, nil
} }
@ -440,17 +465,18 @@ func (p sshFxpHardlinkPacket) id() uint32 { return p.ID }
func (p sshFxpHardlinkPacket) MarshalBinary() ([]byte, error) { func (p sshFxpHardlinkPacket) MarshalBinary() ([]byte, error) {
const ext = "hardlink@openssh.com" const ext = "hardlink@openssh.com"
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(ext) + 4 + len(ext) +
4 + len(p.Oldpath) + 4 + len(p.Oldpath) +
4 + len(p.Newpath) 4 + len(p.Newpath)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpExtended) b = append(b, sshFxpExtended)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, ext) b = marshalString(b, ext)
b = marshalString(b, p.Oldpath) b = marshalString(b, p.Oldpath)
b = marshalString(b, p.Newpath) b = marshalString(b, p.Newpath)
return b, nil return b, nil
} }
@ -462,7 +488,7 @@ type sshFxpReadlinkPacket struct {
func (p sshFxpReadlinkPacket) id() uint32 { return p.ID } func (p sshFxpReadlinkPacket) id() uint32 { return p.ID }
func (p sshFxpReadlinkPacket) MarshalBinary() ([]byte, error) { func (p sshFxpReadlinkPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpReadlink, p.ID, p.Path) return marshalIDStringPacket(sshFxpReadlink, p.ID, p.Path)
} }
func (p *sshFxpReadlinkPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpReadlinkPacket) UnmarshalBinary(b []byte) error {
@ -477,7 +503,7 @@ type sshFxpRealpathPacket struct {
func (p sshFxpRealpathPacket) id() uint32 { return p.ID } func (p sshFxpRealpathPacket) id() uint32 { return p.ID }
func (p sshFxpRealpathPacket) MarshalBinary() ([]byte, error) { func (p sshFxpRealpathPacket) MarshalBinary() ([]byte, error) {
return marshalIDString(sshFxpRealpath, p.ID, p.Path) return marshalIDStringPacket(sshFxpRealpath, p.ID, p.Path)
} }
func (p *sshFxpRealpathPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpRealpathPacket) UnmarshalBinary(b []byte) error {
@ -491,7 +517,7 @@ type sshFxpNameAttr struct {
} }
func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) { func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) {
b := []byte{} var b []byte
b = marshalString(b, p.Name) b = marshalString(b, p.Name)
b = marshalString(b, p.LongName) b = marshalString(b, p.LongName)
for _, attr := range p.Attrs { for _, attr := range p.Attrs {
@ -505,20 +531,31 @@ type sshFxpNamePacket struct {
NameAttrs []sshFxpNameAttr NameAttrs []sshFxpNameAttr
} }
func (p sshFxpNamePacket) MarshalBinary() ([]byte, error) { func (p sshFxpNamePacket) marshalPacket() ([]byte, []byte, error) {
b := []byte{} l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4
b := make([]byte, 4, l)
b = append(b, sshFxpName) b = append(b, sshFxpName)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalUint32(b, uint32(len(p.NameAttrs))) b = marshalUint32(b, uint32(len(p.NameAttrs)))
var payload []byte
for _, na := range p.NameAttrs { for _, na := range p.NameAttrs {
ab, err := na.MarshalBinary() ab, err := na.MarshalBinary()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
b = append(b, ab...) payload = append(payload, ab...)
} }
return b, nil
return b, payload, nil
}
func (p sshFxpNamePacket) MarshalBinary() ([]byte, error) {
header, payload, err := p.marshalPacket()
return append(header, payload...), err
} }
type sshFxpOpenPacket struct { type sshFxpOpenPacket struct {
@ -531,16 +568,17 @@ type sshFxpOpenPacket struct {
func (p sshFxpOpenPacket) id() uint32 { return p.ID } func (p sshFxpOpenPacket) id() uint32 { return p.ID }
func (p sshFxpOpenPacket) MarshalBinary() ([]byte, error) { func (p sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Path) + 4 + len(p.Path) +
4 + 4 4 + 4
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpOpen) b = append(b, sshFxpOpen)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Path) b = marshalString(b, p.Path)
b = marshalUint32(b, p.Pflags) b = marshalUint32(b, p.Pflags)
b = marshalUint32(b, p.Flags) b = marshalUint32(b, p.Flags)
return b, nil return b, nil
} }
@ -568,16 +606,17 @@ type sshFxpReadPacket struct {
func (p sshFxpReadPacket) id() uint32 { return p.ID } func (p sshFxpReadPacket) id() uint32 { return p.ID }
func (p sshFxpReadPacket) MarshalBinary() ([]byte, error) { func (p sshFxpReadPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Handle) + 4 + len(p.Handle) +
8 + 4 // uint64 + uint32 8 + 4 // uint64 + uint32
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpRead) b = append(b, sshFxpRead)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Handle) b = marshalString(b, p.Handle)
b = marshalUint64(b, p.Offset) b = marshalUint64(b, p.Offset)
b = marshalUint32(b, p.Len) b = marshalUint32(b, p.Len)
return b, nil return b, nil
} }
@ -595,16 +634,19 @@ func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error {
return nil return nil
} }
// We need allocate bigger slices with extra capacity to avoid a re-allocation in sshFxpDataPacket.MarshalBinary
// So, we need: uint32(length) + byte(type) + uint32(id) + uint32(data_length)
const dataHeaderLen = 4 + 1 + 4 + 4
func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32) []byte { func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32) []byte {
dataLen := clamp(p.Len, maxTxPacket) dataLen := clamp(p.Len, maxTxPacket)
if alloc != nil { if alloc != nil {
// GetPage returns a slice with capacity = maxMsgLength this is enough to avoid new allocations in // GetPage returns a slice with capacity = maxMsgLength this is enough to avoid new allocations in
// sshFxpDataPacket.MarshalBinary and sendPacket // sshFxpDataPacket.MarshalBinary
return alloc.GetPage(orderID)[:dataLen] return alloc.GetPage(orderID)[:dataLen]
} }
// we allocate a slice with a bigger capacity so we avoid a new allocation in sshFxpDataPacket.MarshalBinary // allocate with extra space for the header
// and in sendPacket, we need 9 bytes in MarshalBinary and 4 bytes in sendPacket. return make([]byte, dataLen, dataLen+dataHeaderLen)
return make([]byte, dataLen, dataLen+9+4)
} }
type sshFxpRenamePacket struct { type sshFxpRenamePacket struct {
@ -616,15 +658,16 @@ type sshFxpRenamePacket struct {
func (p sshFxpRenamePacket) id() uint32 { return p.ID } func (p sshFxpRenamePacket) id() uint32 { return p.ID }
func (p sshFxpRenamePacket) MarshalBinary() ([]byte, error) { func (p sshFxpRenamePacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Oldpath) + 4 + len(p.Oldpath) +
4 + len(p.Newpath) 4 + len(p.Newpath)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpRename) b = append(b, sshFxpRename)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Oldpath) b = marshalString(b, p.Oldpath)
b = marshalString(b, p.Newpath) b = marshalString(b, p.Newpath)
return b, nil return b, nil
} }
@ -650,17 +693,18 @@ func (p sshFxpPosixRenamePacket) id() uint32 { return p.ID }
func (p sshFxpPosixRenamePacket) MarshalBinary() ([]byte, error) { func (p sshFxpPosixRenamePacket) MarshalBinary() ([]byte, error) {
const ext = "posix-rename@openssh.com" const ext = "posix-rename@openssh.com"
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(ext) + 4 + len(ext) +
4 + len(p.Oldpath) + 4 + len(p.Oldpath) +
4 + len(p.Newpath) 4 + len(p.Newpath)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpExtended) b = append(b, sshFxpExtended)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, ext) b = marshalString(b, ext)
b = marshalString(b, p.Oldpath) b = marshalString(b, p.Oldpath)
b = marshalString(b, p.Newpath) b = marshalString(b, p.Newpath)
return b, nil return b, nil
} }
@ -674,20 +718,25 @@ type sshFxpWritePacket struct {
func (p sshFxpWritePacket) id() uint32 { return p.ID } func (p sshFxpWritePacket) id() uint32 { return p.ID }
func (p sshFxpWritePacket) MarshalBinary() ([]byte, error) { func (p sshFxpWritePacket) marshalPacket() ([]byte, []byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Handle) + 4 + len(p.Handle) +
8 + 4 + // uint64 + uint32 8 + // uint64
len(p.Data) 4
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpWrite) b = append(b, sshFxpWrite)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Handle) b = marshalString(b, p.Handle)
b = marshalUint64(b, p.Offset) b = marshalUint64(b, p.Offset)
b = marshalUint32(b, p.Length) b = marshalUint32(b, p.Length)
b = append(b, p.Data...)
return b, nil return b, p.Data, nil
}
func (p sshFxpWritePacket) MarshalBinary() ([]byte, error) {
header, payload, err := p.marshalPacket()
return append(header, payload...), err
} }
func (p *sshFxpWritePacket) UnmarshalBinary(b []byte) error { func (p *sshFxpWritePacket) UnmarshalBinary(b []byte) error {
@ -717,15 +766,16 @@ type sshFxpMkdirPacket struct {
func (p sshFxpMkdirPacket) id() uint32 { return p.ID } func (p sshFxpMkdirPacket) id() uint32 { return p.ID }
func (p sshFxpMkdirPacket) MarshalBinary() ([]byte, error) { func (p sshFxpMkdirPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Path) + 4 + len(p.Path) +
4 // uint32 4 // uint32
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpMkdir) b = append(b, sshFxpMkdir)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Path) b = marshalString(b, p.Path)
b = marshalUint32(b, p.Flags) b = marshalUint32(b, p.Flags)
return b, nil return b, nil
} }
@ -758,32 +808,46 @@ type sshFxpFsetstatPacket struct {
func (p sshFxpSetstatPacket) id() uint32 { return p.ID } func (p sshFxpSetstatPacket) id() uint32 { return p.ID }
func (p sshFxpFsetstatPacket) id() uint32 { return p.ID } func (p sshFxpFsetstatPacket) id() uint32 { return p.ID }
func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) { func (p sshFxpSetstatPacket) marshalPacket() ([]byte, []byte, error) {
l := 1 + 4 + // type(byte) + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Path) + 4 + len(p.Path) +
4 // uint32 + uint64 4 // uint32
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpSetstat) b = append(b, sshFxpSetstat)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Path) b = marshalString(b, p.Path)
b = marshalUint32(b, p.Flags) b = marshalUint32(b, p.Flags)
b = marshal(b, p.Attrs)
return b, nil payload := marshal(nil, p.Attrs)
return b, payload, nil
} }
func (p sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) { func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 header, payload, err := p.marshalPacket()
4 + len(p.Handle) + return append(header, payload...), err
4 // uint32 + uint64 }
b := make([]byte, 0, l) func (p sshFxpFsetstatPacket) marshalPacket() ([]byte, []byte, error) {
l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Handle) +
4 // uint32
b := make([]byte, 4, l)
b = append(b, sshFxpFsetstat) b = append(b, sshFxpFsetstat)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Handle) b = marshalString(b, p.Handle)
b = marshalUint32(b, p.Flags) b = marshalUint32(b, p.Flags)
b = marshal(b, p.Attrs)
return b, nil payload := marshal(nil, p.Attrs)
return b, payload, nil
}
func (p sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) {
header, payload, err := p.marshalPacket()
return append(header, payload...), err
} }
func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error { func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error {
@ -818,9 +882,14 @@ type sshFxpHandlePacket struct {
} }
func (p sshFxpHandlePacket) MarshalBinary() ([]byte, error) { func (p sshFxpHandlePacket) MarshalBinary() ([]byte, error) {
b := []byte{sshFxpHandle} l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(p.Handle)
b := make([]byte, 4, l)
b = append(b, sshFxpHandle)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Handle) b = marshalString(b, p.Handle)
return b, nil return b, nil
} }
@ -830,9 +899,16 @@ type sshFxpStatusPacket struct {
} }
func (p sshFxpStatusPacket) MarshalBinary() ([]byte, error) { func (p sshFxpStatusPacket) MarshalBinary() ([]byte, error) {
b := []byte{sshFxpStatus} l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 +
4 + len(p.StatusError.msg) +
4 + len(p.StatusError.lang)
b := make([]byte, 4, l)
b = append(b, sshFxpStatus)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalStatus(b, p.StatusError) b = marshalStatus(b, p.StatusError)
return b, nil return b, nil
} }
@ -842,14 +918,30 @@ type sshFxpDataPacket struct {
Data []byte Data []byte
} }
func (p sshFxpDataPacket) marshalPacket() ([]byte, []byte, error) {
l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4
b := make([]byte, 4, l)
b = append(b, sshFxpData)
b = marshalUint32(b, p.ID)
b = marshalUint32(b, p.Length)
return b, p.Data, nil
}
// MarshalBinary encodes the receiver into a binary form and returns the result. // MarshalBinary encodes the receiver into a binary form and returns the result.
// To avoid a new allocation the Data slice must have a capacity >= Length + 9 // To avoid a new allocation the Data slice must have a capacity >= Length + 9
//
// This is hand-coded rather than just append(header, payload...),
// in order to try and reuse the r.Data backing store in the packet.
func (p sshFxpDataPacket) MarshalBinary() ([]byte, error) { func (p sshFxpDataPacket) MarshalBinary() ([]byte, error) {
b := append(p.Data, make([]byte, 9)...) b := append(p.Data, make([]byte, dataHeaderLen)...)
copy(b[9:], p.Data[:p.Length]) copy(b[dataHeaderLen:], p.Data[:p.Length])
b[0] = sshFxpData // b[0:4] will be overwritten with the length in sendPacket
binary.BigEndian.PutUint32(b[1:5], p.ID) b[4] = sshFxpData
binary.BigEndian.PutUint32(b[5:9], p.Length) binary.BigEndian.PutUint32(b[5:9], p.ID)
binary.BigEndian.PutUint32(b[9:13], p.Length)
return b, nil return b, nil
} }
@ -875,15 +967,17 @@ type sshFxpStatvfsPacket struct {
func (p sshFxpStatvfsPacket) id() uint32 { return p.ID } func (p sshFxpStatvfsPacket) id() uint32 { return p.ID }
func (p sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) { func (p sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type(byte) + uint32 const ext = "statvfs@openssh.com"
len(p.Path) + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
len("statvfs@openssh.com") 4 + len(ext) +
4 + len(p.Path)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpExtended) b = append(b, sshFxpExtended)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, "statvfs@openssh.com") b = marshalString(b, ext)
b = marshalString(b, p.Path) b = marshalString(b, p.Path)
return b, nil return b, nil
} }
@ -913,12 +1007,19 @@ func (p *StatVFS) FreeSpace() uint64 {
return p.Frsize * p.Bfree return p.Frsize * p.Bfree
} }
// MarshalBinary converts to ssh_FXP_EXTENDED_REPLY packet binary format // marshalPacket converts to ssh_FXP_EXTENDED_REPLY packet binary format
func (p *StatVFS) MarshalBinary() ([]byte, error) { func (p *StatVFS) marshalPacket() ([]byte, []byte, error) {
header := []byte{0, 0, 0, 0, sshFxpExtendedReply}
var buf bytes.Buffer var buf bytes.Buffer
buf.Write([]byte{sshFxpExtendedReply})
err := binary.Write(&buf, binary.BigEndian, p) err := binary.Write(&buf, binary.BigEndian, p)
return buf.Bytes(), err
return header, buf.Bytes(), err
}
func (p *StatVFS) MarshalBinary() ([]byte, error) {
header, payload, err := p.marshalPacket()
return append(header, payload...), err
} }
type sshFxpFsyncPacket struct { type sshFxpFsyncPacket struct {
@ -929,15 +1030,17 @@ type sshFxpFsyncPacket struct {
func (p sshFxpFsyncPacket) id() uint32 { return p.ID } func (p sshFxpFsyncPacket) id() uint32 { return p.ID }
func (p sshFxpFsyncPacket) MarshalBinary() ([]byte, error) { func (p sshFxpFsyncPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + // type (byte) + ID (uint32) const ext = "fsync@openssh.com"
4 + len("fsync@openssh.com") + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
4 + len(ext) +
4 + len(p.Handle) 4 + len(p.Handle)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpExtended) b = append(b, sshFxpExtended)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, "fsync@openssh.com") b = marshalString(b, ext)
b = marshalString(b, p.Handle) b = marshalString(b, p.Handle)
return b, nil return b, nil
} }

View File

@ -382,11 +382,22 @@ type sshFxpStatResponse struct {
info os.FileInfo info os.FileInfo
} }
func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) { func (p sshFxpStatResponse) marshalPacket() ([]byte, []byte, error) {
b := []byte{sshFxpAttrs} l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(id)
b := make([]byte, 4, l)
b = append(b, sshFxpAttrs)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalFileInfo(b, p.info)
return b, nil var payload []byte
payload = marshalFileInfo(payload, p.info)
return b, payload, nil
}
func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) {
header, payload, err := p.marshalPacket()
return append(header, payload...), err
} }
var emptyFileStat = []interface{}{uint32(0)} var emptyFileStat = []interface{}{uint32(0)}

View File

@ -193,15 +193,16 @@ type sshFxpTestBadExtendedPacket struct {
func (p sshFxpTestBadExtendedPacket) id() uint32 { return p.ID } func (p sshFxpTestBadExtendedPacket) id() uint32 { return p.ID }
func (p sshFxpTestBadExtendedPacket) MarshalBinary() ([]byte, error) { func (p sshFxpTestBadExtendedPacket) MarshalBinary() ([]byte, error) {
l := 1 + 4 + 4 + // type(byte) + uint32 + uint32 l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
len(p.Extension) + 4 + len(p.Extension) +
len(p.Data) 4 + len(p.Data)
b := make([]byte, 0, l) b := make([]byte, 4, l)
b = append(b, sshFxpExtended) b = append(b, sshFxpExtended)
b = marshalUint32(b, p.ID) b = marshalUint32(b, p.ID)
b = marshalString(b, p.Extension) b = marshalString(b, p.Extension)
b = marshalString(b, p.Data) b = marshalString(b, p.Data)
return b, nil return b, nil
} }