From 5e8f9f4960b1e1e0644e031a473b1f6046fc9ec8 Mon Sep 17 00:00:00 2001 From: Cassondra Foesch Date: Wed, 28 Oct 2020 20:54:31 +0000 Subject: [PATCH] MarshalBinary now gives a 4-byte header for length, marshalPacket gives a two-stage write --- packet-manager_test.go | 2 +- packet.go | 289 ++++++++++++++++++++++++++++------------- server.go | 19 ++- server_test.go | 9 +- 4 files changed, 217 insertions(+), 102 deletions(-) diff --git a/packet-manager_test.go b/packet-manager_test.go index 652910b..e43a4cf 100644 --- a/packet-manager_test.go +++ b/packet-manager_test.go @@ -31,7 +31,7 @@ func fake(rid, order uint32) fakepacket { } func (fakepacket) MarshalBinary() ([]byte, error) { - return []byte{}, nil + return make([]byte, 4), nil } func (fakepacket) UnmarshalBinary([]byte) error { diff --git a/packet.go b/packet.go index 6bbbe22..55a6021 100644 --- a/packet.go +++ b/packet.go @@ -116,26 +116,45 @@ func unmarshalStringSafe(b []byte) (string, []byte, error) { 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. func sendPacket(w io.Writer, m encoding.BinaryMarshaler) error { - bb, err := m.MarshalBinary() + header, payload, err := marshalPacket(m) if err != nil { 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) - if err != nil { + length := len(header) + len(payload) - 4 // subtract the uint32(length) from the start + 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) } + + if len(payload) > 0 { + if _, err := w.Write(payload); err != nil { + return errors.Errorf("failed to send packet payload: %v", err) + } + } + return nil } @@ -200,18 +219,20 @@ type sshFxInitPacket struct { } 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 { l += 4 + len(e.Name) + 4 + len(e.Data) } - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpInit) b = marshalUint32(b, p.Version) + for _, e := range p.Extensions { b = marshalString(b, e.Name) b = marshalString(b, e.Data) } + return b, nil } @@ -241,29 +262,32 @@ type sshExtensionPair struct { } 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 { l += 4 + len(e.Name) + 4 + len(e.Data) } - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpVersion) b = marshalUint32(b, p.Version) + for _, e := range p.Extensions { b = marshalString(b, e.Name) b = marshalString(b, e.Data) } + return b, nil } -func marshalIDString(packetType byte, id uint32, str string) ([]byte, error) { - l := 1 + 4 + // type(byte) + uint32 +func marshalIDStringPacket(packetType byte, id uint32, str string) ([]byte, error) { + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) 4 + len(str) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, packetType) b = marshalUint32(b, id) b = marshalString(b, str) + return b, nil } @@ -285,7 +309,7 @@ type sshFxpReaddirPacket struct { func (p sshFxpReaddirPacket) id() uint32 { return p.ID } 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 { @@ -300,7 +324,7 @@ type sshFxpOpendirPacket struct { func (p sshFxpOpendirPacket) id() uint32 { return p.ID } 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 { @@ -315,7 +339,7 @@ type sshFxpLstatPacket struct { func (p sshFxpLstatPacket) id() uint32 { return p.ID } 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 { @@ -330,7 +354,7 @@ type sshFxpStatPacket struct { func (p sshFxpStatPacket) id() uint32 { return p.ID } 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 { @@ -345,7 +369,7 @@ type sshFxpFstatPacket struct { func (p sshFxpFstatPacket) id() uint32 { return p.ID } 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 { @@ -360,7 +384,7 @@ type sshFxpClosePacket struct { func (p sshFxpClosePacket) id() uint32 { return p.ID } 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 { @@ -375,7 +399,7 @@ type sshFxpRemovePacket struct { func (p sshFxpRemovePacket) id() uint32 { return p.ID } 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 { @@ -390,7 +414,7 @@ type sshFxpRmdirPacket struct { func (p sshFxpRmdirPacket) id() uint32 { return p.ID } 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 { @@ -406,15 +430,16 @@ type sshFxpSymlinkPacket struct { func (p sshFxpSymlinkPacket) id() uint32 { return p.ID } 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.Linkpath) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpSymlink) b = marshalUint32(b, p.ID) b = marshalString(b, p.Targetpath) b = marshalString(b, p.Linkpath) + return b, nil } @@ -440,17 +465,18 @@ func (p sshFxpHardlinkPacket) id() uint32 { return p.ID } func (p sshFxpHardlinkPacket) MarshalBinary() ([]byte, error) { 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(p.Oldpath) + 4 + len(p.Newpath) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpExtended) b = marshalUint32(b, p.ID) b = marshalString(b, ext) b = marshalString(b, p.Oldpath) b = marshalString(b, p.Newpath) + return b, nil } @@ -462,7 +488,7 @@ type sshFxpReadlinkPacket struct { func (p sshFxpReadlinkPacket) id() uint32 { return p.ID } 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 { @@ -477,7 +503,7 @@ type sshFxpRealpathPacket struct { func (p sshFxpRealpathPacket) id() uint32 { return p.ID } 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 { @@ -491,7 +517,7 @@ type sshFxpNameAttr struct { } func (p sshFxpNameAttr) MarshalBinary() ([]byte, error) { - b := []byte{} + var b []byte b = marshalString(b, p.Name) b = marshalString(b, p.LongName) for _, attr := range p.Attrs { @@ -505,20 +531,31 @@ type sshFxpNamePacket struct { NameAttrs []sshFxpNameAttr } -func (p sshFxpNamePacket) MarshalBinary() ([]byte, error) { - b := []byte{} +func (p sshFxpNamePacket) marshalPacket() ([]byte, []byte, error) { + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) + 4 + + b := make([]byte, 4, l) b = append(b, sshFxpName) b = marshalUint32(b, p.ID) b = marshalUint32(b, uint32(len(p.NameAttrs))) + + var payload []byte for _, na := range p.NameAttrs { ab, err := na.MarshalBinary() 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 { @@ -531,16 +568,17 @@ type sshFxpOpenPacket struct { func (p sshFxpOpenPacket) id() uint32 { return p.ID } func (p sshFxpOpenPacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) 4 + len(p.Path) + 4 + 4 - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpOpen) b = marshalUint32(b, p.ID) b = marshalString(b, p.Path) b = marshalUint32(b, p.Pflags) b = marshalUint32(b, p.Flags) + return b, nil } @@ -568,16 +606,17 @@ type sshFxpReadPacket struct { func (p sshFxpReadPacket) id() uint32 { return p.ID } 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) + 8 + 4 // uint64 + uint32 - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpRead) b = marshalUint32(b, p.ID) b = marshalString(b, p.Handle) b = marshalUint64(b, p.Offset) b = marshalUint32(b, p.Len) + return b, nil } @@ -595,16 +634,19 @@ func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error { 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 { dataLen := clamp(p.Len, maxTxPacket) if alloc != nil { // 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] } - // we allocate a slice with a bigger capacity so we avoid a new allocation in sshFxpDataPacket.MarshalBinary - // and in sendPacket, we need 9 bytes in MarshalBinary and 4 bytes in sendPacket. - return make([]byte, dataLen, dataLen+9+4) + // allocate with extra space for the header + return make([]byte, dataLen, dataLen+dataHeaderLen) } type sshFxpRenamePacket struct { @@ -616,15 +658,16 @@ type sshFxpRenamePacket struct { func (p sshFxpRenamePacket) id() uint32 { return p.ID } 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.Newpath) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpRename) b = marshalUint32(b, p.ID) b = marshalString(b, p.Oldpath) b = marshalString(b, p.Newpath) + return b, nil } @@ -650,17 +693,18 @@ func (p sshFxpPosixRenamePacket) id() uint32 { return p.ID } func (p sshFxpPosixRenamePacket) MarshalBinary() ([]byte, error) { 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(p.Oldpath) + 4 + len(p.Newpath) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpExtended) b = marshalUint32(b, p.ID) b = marshalString(b, ext) b = marshalString(b, p.Oldpath) b = marshalString(b, p.Newpath) + return b, nil } @@ -674,20 +718,25 @@ type sshFxpWritePacket struct { func (p sshFxpWritePacket) id() uint32 { return p.ID } -func (p sshFxpWritePacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + // type(byte) + uint32 +func (p sshFxpWritePacket) marshalPacket() ([]byte, []byte, error) { + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) 4 + len(p.Handle) + - 8 + 4 + // uint64 + uint32 - len(p.Data) + 8 + // uint64 + 4 - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpWrite) b = marshalUint32(b, p.ID) b = marshalString(b, p.Handle) b = marshalUint64(b, p.Offset) 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 { @@ -717,15 +766,16 @@ type sshFxpMkdirPacket struct { func (p sshFxpMkdirPacket) id() uint32 { return p.ID } 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 // uint32 - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpMkdir) b = marshalUint32(b, p.ID) b = marshalString(b, p.Path) b = marshalUint32(b, p.Flags) + return b, nil } @@ -758,32 +808,46 @@ type sshFxpFsetstatPacket struct { func (p sshFxpSetstatPacket) id() uint32 { return p.ID } func (p sshFxpFsetstatPacket) id() uint32 { return p.ID } -func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + // type(byte) + uint32 +func (p sshFxpSetstatPacket) marshalPacket() ([]byte, []byte, error) { + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) 4 + len(p.Path) + - 4 // uint32 + uint64 + 4 // uint32 - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpSetstat) b = marshalUint32(b, p.ID) b = marshalString(b, p.Path) 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) { - l := 1 + 4 + // type(byte) + uint32 - 4 + len(p.Handle) + - 4 // uint32 + uint64 +func (p sshFxpSetstatPacket) MarshalBinary() ([]byte, error) { + header, payload, err := p.marshalPacket() + return append(header, payload...), err +} - 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 = marshalUint32(b, p.ID) b = marshalString(b, p.Handle) 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 { @@ -818,9 +882,14 @@ type sshFxpHandlePacket struct { } 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 = marshalString(b, p.Handle) + return b, nil } @@ -830,9 +899,16 @@ type sshFxpStatusPacket struct { } 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 = marshalStatus(b, p.StatusError) + return b, nil } @@ -842,14 +918,30 @@ type sshFxpDataPacket struct { 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. // 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) { - b := append(p.Data, make([]byte, 9)...) - copy(b[9:], p.Data[:p.Length]) - b[0] = sshFxpData - binary.BigEndian.PutUint32(b[1:5], p.ID) - binary.BigEndian.PutUint32(b[5:9], p.Length) + b := append(p.Data, make([]byte, dataHeaderLen)...) + copy(b[dataHeaderLen:], p.Data[:p.Length]) + // b[0:4] will be overwritten with the length in sendPacket + b[4] = sshFxpData + binary.BigEndian.PutUint32(b[5:9], p.ID) + binary.BigEndian.PutUint32(b[9:13], p.Length) return b, nil } @@ -875,15 +967,17 @@ type sshFxpStatvfsPacket struct { func (p sshFxpStatvfsPacket) id() uint32 { return p.ID } func (p sshFxpStatvfsPacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + // type(byte) + uint32 - len(p.Path) + - len("statvfs@openssh.com") + const ext = "statvfs@openssh.com" + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) + 4 + len(ext) + + 4 + len(p.Path) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpExtended) b = marshalUint32(b, p.ID) - b = marshalString(b, "statvfs@openssh.com") + b = marshalString(b, ext) b = marshalString(b, p.Path) + return b, nil } @@ -913,12 +1007,19 @@ func (p *StatVFS) FreeSpace() uint64 { return p.Frsize * p.Bfree } -// MarshalBinary converts to ssh_FXP_EXTENDED_REPLY packet binary format -func (p *StatVFS) MarshalBinary() ([]byte, error) { +// marshalPacket converts to ssh_FXP_EXTENDED_REPLY packet binary format +func (p *StatVFS) marshalPacket() ([]byte, []byte, error) { + header := []byte{0, 0, 0, 0, sshFxpExtendedReply} + var buf bytes.Buffer - buf.Write([]byte{sshFxpExtendedReply}) 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 { @@ -929,15 +1030,17 @@ type sshFxpFsyncPacket struct { func (p sshFxpFsyncPacket) id() uint32 { return p.ID } func (p sshFxpFsyncPacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + // type (byte) + ID (uint32) - 4 + len("fsync@openssh.com") + + const ext = "fsync@openssh.com" + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) + 4 + len(ext) + 4 + len(p.Handle) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpExtended) b = marshalUint32(b, p.ID) - b = marshalString(b, "fsync@openssh.com") + b = marshalString(b, ext) b = marshalString(b, p.Handle) + return b, nil } diff --git a/server.go b/server.go index f595518..9502932 100644 --- a/server.go +++ b/server.go @@ -382,11 +382,22 @@ type sshFxpStatResponse struct { info os.FileInfo } -func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) { - b := []byte{sshFxpAttrs} +func (p sshFxpStatResponse) marshalPacket() ([]byte, []byte, error) { + 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 = 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)} diff --git a/server_test.go b/server_test.go index 815eed2..7347e20 100644 --- a/server_test.go +++ b/server_test.go @@ -193,15 +193,16 @@ type sshFxpTestBadExtendedPacket struct { func (p sshFxpTestBadExtendedPacket) id() uint32 { return p.ID } func (p sshFxpTestBadExtendedPacket) MarshalBinary() ([]byte, error) { - l := 1 + 4 + 4 + // type(byte) + uint32 + uint32 - len(p.Extension) + - len(p.Data) + l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) + 4 + len(p.Extension) + + 4 + len(p.Data) - b := make([]byte, 0, l) + b := make([]byte, 4, l) b = append(b, sshFxpExtended) b = marshalUint32(b, p.ID) b = marshalString(b, p.Extension) b = marshalString(b, p.Data) + return b, nil }