package filexfer import ( "encoding" "sync" ) // ExtendedData aliases the untyped interface composition of encoding.BinaryMarshaler and encoding.BinaryUnmarshaler. type ExtendedData = interface { encoding.BinaryMarshaler encoding.BinaryUnmarshaler } // ExtendedDataConstructor defines a function that returns a new(ArbitraryExtendedPacket). type ExtendedDataConstructor func() ExtendedData var extendedPacketTypes = struct { mu sync.RWMutex constructors map[string]ExtendedDataConstructor }{ constructors: make(map[string]ExtendedDataConstructor), } // RegisterExtendedPacketType defines a specific ExtendedDataConstructor for the given extension string. func RegisterExtendedPacketType(extension string, constructor ExtendedDataConstructor) { extendedPacketTypes.mu.Lock() defer extendedPacketTypes.mu.Unlock() if _, exist := extendedPacketTypes.constructors[extension]; exist { panic("encoding/ssh/filexfer: multiple registration of extended packet type " + extension) } extendedPacketTypes.constructors[extension] = constructor } func newExtendedPacket(extension string) ExtendedData { extendedPacketTypes.mu.RLock() defer extendedPacketTypes.mu.RUnlock() if f := extendedPacketTypes.constructors[extension]; f != nil { return f() } return new(Buffer) } // ExtendedPacket defines the SSH_FXP_CLOSE packet. type ExtendedPacket struct { ExtendedRequest string Data ExtendedData } // Type returns the SSH_FXP_xy value associated with this packet type. func (p *ExtendedPacket) Type() PacketType { return PacketTypeExtended } // MarshalPacket returns p as a two-part binary encoding of p. // // The Data is marshaled into binary, and returned as the payload. func (p *ExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { buf := NewBuffer(b) if buf.Cap() < 9 { size := 4 + len(p.ExtendedRequest) // string(extended-request) buf = NewMarshalBuffer(size) } buf.StartPacket(PacketTypeExtended, reqid) buf.AppendString(p.ExtendedRequest) if p.Data != nil { payload, err = p.Data.MarshalBinary() if err != nil { return nil, nil, err } } return buf.Packet(payload) } // UnmarshalPacketBody unmarshals the packet body from the given Buffer. // It is assumed that the uint32(request-id) has already been consumed. // // If p.Data is nil, and the extension has been registered, a new type will be made from the registration. // 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. func (p *ExtendedPacket) UnmarshalPacketBody(buf *Buffer) (err error) { if p.ExtendedRequest, err = buf.ConsumeString(); err != nil { return err } if p.Data == nil { p.Data = newExtendedPacket(p.ExtendedRequest) } return p.Data.UnmarshalBinary(buf.Bytes()) } // ExtendedReplyPacket defines the SSH_FXP_CLOSE packet. type ExtendedReplyPacket struct { Data ExtendedData } // Type returns the SSH_FXP_xy value associated with this packet type. func (p *ExtendedReplyPacket) Type() PacketType { return PacketTypeExtendedReply } // MarshalPacket returns p as a two-part binary encoding of p. // // The Data is marshaled into binary, and returned as the payload. func (p *ExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) { buf := NewBuffer(b) if buf.Cap() < 9 { buf = NewMarshalBuffer(0) } buf.StartPacket(PacketTypeExtendedReply, reqid) if p.Data != nil { payload, err = p.Data.MarshalBinary() if err != nil { return nil, nil, err } } return buf.Packet(payload) } // UnmarshalPacketBody unmarshals the packet body from the given Buffer. // It is assumed that the uint32(request-id) has already been consumed. // // If p.Data is nil, and there is request-specific-data, // then the request-specific-data will be wrapped in a Buffer and assigned to p.Data. func (p *ExtendedReplyPacket) UnmarshalPacketBody(buf *Buffer) (err error) { if p.Data == nil { p.Data = new(Buffer) } return p.Data.UnmarshalBinary(buf.Bytes()) }