github.com/pkg/sftp@v1.13.6/internal/encoding/ssh/filexfer/extended_packets.go (about) 1 package sshfx 2 3 import ( 4 "encoding" 5 "sync" 6 ) 7 8 // ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler. 9 type ExtendedData = interface { 10 encoding.BinaryMarshaler 11 encoding.BinaryUnmarshaler 12 } 13 14 // ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket). 15 type ExtendedDataConstructor func() ExtendedData 16 17 var extendedPacketTypes = struct { 18 mu sync.RWMutex 19 constructors map[string]ExtendedDataConstructor 20 }{ 21 constructors: make(map[string]ExtendedDataConstructor), 22 } 23 24 // RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string. 25 func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) { 26 extendedPacketTypes.mu.Lock() 27 defer extendedPacketTypes.mu.Unlock() 28 29 if _, exist := extendedPacketTypes.constructors[extension]; exist { 30 panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension) 31 } 32 33 extendedPacketTypes.constructors[extension] = constructor 34 } 35 36 func newExtendedPacket(extension string) ExtendedData { 37 extendedPacketTypes.mu.RLock() 38 defer extendedPacketTypes.mu.RUnlock() 39 40 if f := extendedPacketTypes.constructors[extension]; f != nil { 41 return f() 42 } 43 44 return new(Buffer) 45 } 46 47 // ExtendedPacket defines the SSH_FXP_CLOSE packet. 48 type ExtendedPacket struct { 49 ExtendedRequest string 50 51 Data ExtendedData 52 } 53 54 // Type returns the SSH_FXP_xy value associated with this packet type. 55 func (p *ExtendedPacket) Type() PacketType { 56 return PacketTypeExtended 57 } 58 59 // MarshalPacket returns p as a two-part binary encoding of p. 60 // 61 // The Data is marshaled into binary, and returned as the payload. 62 func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 63 buf := NewBuffer(b) 64 if buf.Cap() < 9 { 65 size := 4 + len(p.ExtendedRequest) // string(extended-request) 66 buf = NewMarshalBuffer(size) 67 } 68 69 buf.StartPacket(PacketTypeExtended, reqid) 70 buf.AppendString(p.ExtendedRequest) 71 72 if p.Data != nil { 73 payload, err = p.Data.MarshalBinary() 74 if err != nil { 75 return nil, nil, err 76 } 77 } 78 79 return buf.Packet(payload) 80 } 81 82 // UnmarshalPacketBody unmarshals the packet body from the given Buffer. 83 // It is assumed that the uint32(request-id) has already been consumed. 84 // 85 // If p.Data is nil, and the extension has been registered, a new type will be made from the registration. 86 // If the extension has not been registered, then a new Buffer will be allocated. 87 // Then the request-specific-data will be unmarshaled from the rest of the buffer. 88 func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) { 89 p.ExtendedRequest = buf.ConsumeString() 90 if buf.Err != nil { 91 return buf.Err 92 } 93 94 if p.Data == nil { 95 p.Data = newExtendedPacket(p.ExtendedRequest) 96 } 97 98 return p.Data.UnmarshalBinary(buf.Bytes()) 99 } 100 101 // ExtendedReplyPacket defines the SSH_FXP_CLOSE packet. 102 type ExtendedReplyPacket struct { 103 Data ExtendedData 104 } 105 106 // Type returns the SSH_FXP_xy value associated with this packet type. 107 func (p *ExtendedReplyPacket) Type() PacketType { 108 return PacketTypeExtendedReply 109 } 110 111 // MarshalPacket returns p as a two-part binary encoding of p. 112 // 113 // The Data is marshaled into binary, and returned as the payload. 114 func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { 115 buf := NewBuffer(b) 116 if buf.Cap() < 9 { 117 buf = NewMarshalBuffer(0) 118 } 119 120 buf.StartPacket(PacketTypeExtendedReply, reqid) 121 122 if p.Data != nil { 123 payload, err = p.Data.MarshalBinary() 124 if err != nil { 125 return nil, nil, err 126 } 127 } 128 129 return buf.Packet(payload) 130 } 131 132 // UnmarshalPacketBody unmarshals the packet body from the given Buffer. 133 // It is assumed that the uint32(request-id) has already been consumed. 134 // 135 // If p.Data is nil, and there is request-specific-data, 136 // then the request-specific-data will be wrapped in a Buffer and assigned to p.Data. 137 func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) { 138 if p.Data == nil { 139 p.Data = new(Buffer) 140 } 141 142 return p.Data.UnmarshalBinary(buf.Bytes()) 143 }